e31bc5637a
Adding a few (FMAf) matcher patterns to the SPARC back-end Reviewed-by: rbackman, kvn
10956 lines
345 KiB
Plaintext
10956 lines
345 KiB
Plaintext
//
|
|
// Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
|
|
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
//
|
|
// This code is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU General Public License version 2 only, as
|
|
// published by the Free Software Foundation.
|
|
//
|
|
// This code is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
// version 2 for more details (a copy is included in the LICENSE file that
|
|
// accompanied this code).
|
|
//
|
|
// You should have received a copy of the GNU General Public License version
|
|
// 2 along with this work; if not, write to the Free Software Foundation,
|
|
// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
//
|
|
// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
// or visit www.oracle.com if you need additional information or have any
|
|
// questions.
|
|
//
|
|
//
|
|
|
|
// SPARC Architecture Description File
|
|
|
|
//----------REGISTER DEFINITION BLOCK------------------------------------------
|
|
// This information is used by the matcher and the register allocator to
|
|
// describe individual registers and classes of registers within the target
|
|
// archtecture.
|
|
register %{
|
|
//----------Architecture Description Register Definitions----------------------
|
|
// General Registers
|
|
// "reg_def" name ( register save type, C convention save type,
|
|
// ideal register type, encoding, vm name );
|
|
// Register Save Types:
|
|
//
|
|
// NS = No-Save: The register allocator assumes that these registers
|
|
// can be used without saving upon entry to the method, &
|
|
// that they do not need to be saved at call sites.
|
|
//
|
|
// SOC = Save-On-Call: The register allocator assumes that these registers
|
|
// can be used without saving upon entry to the method,
|
|
// but that they must be saved at call sites.
|
|
//
|
|
// SOE = Save-On-Entry: The register allocator assumes that these registers
|
|
// must be saved before using them upon entry to the
|
|
// method, but they do not need to be saved at call
|
|
// sites.
|
|
//
|
|
// AS = Always-Save: The register allocator assumes that these registers
|
|
// must be saved before using them upon entry to the
|
|
// method, & that they must be saved at call sites.
|
|
//
|
|
// Ideal Register Type is used to determine how to save & restore a
|
|
// register. Op_RegI will get spilled with LoadI/StoreI, Op_RegP will get
|
|
// spilled with LoadP/StoreP. If the register supports both, use Op_RegI.
|
|
//
|
|
// The encoding number is the actual bit-pattern placed into the opcodes.
|
|
|
|
|
|
// ----------------------------
|
|
// Integer/Long Registers
|
|
// ----------------------------
|
|
|
|
// Need to expose the hi/lo aspect of 64-bit registers
|
|
// This register set is used for both the 64-bit build and
|
|
// the 32-bit build with 1-register longs.
|
|
|
|
// Global Registers 0-7
|
|
reg_def R_G0H( NS, NS, Op_RegI,128, G0->as_VMReg()->next());
|
|
reg_def R_G0 ( NS, NS, Op_RegI, 0, G0->as_VMReg());
|
|
reg_def R_G1H(SOC, SOC, Op_RegI,129, G1->as_VMReg()->next());
|
|
reg_def R_G1 (SOC, SOC, Op_RegI, 1, G1->as_VMReg());
|
|
reg_def R_G2H( NS, NS, Op_RegI,130, G2->as_VMReg()->next());
|
|
reg_def R_G2 ( NS, NS, Op_RegI, 2, G2->as_VMReg());
|
|
reg_def R_G3H(SOC, SOC, Op_RegI,131, G3->as_VMReg()->next());
|
|
reg_def R_G3 (SOC, SOC, Op_RegI, 3, G3->as_VMReg());
|
|
reg_def R_G4H(SOC, SOC, Op_RegI,132, G4->as_VMReg()->next());
|
|
reg_def R_G4 (SOC, SOC, Op_RegI, 4, G4->as_VMReg());
|
|
reg_def R_G5H(SOC, SOC, Op_RegI,133, G5->as_VMReg()->next());
|
|
reg_def R_G5 (SOC, SOC, Op_RegI, 5, G5->as_VMReg());
|
|
reg_def R_G6H( NS, NS, Op_RegI,134, G6->as_VMReg()->next());
|
|
reg_def R_G6 ( NS, NS, Op_RegI, 6, G6->as_VMReg());
|
|
reg_def R_G7H( NS, NS, Op_RegI,135, G7->as_VMReg()->next());
|
|
reg_def R_G7 ( NS, NS, Op_RegI, 7, G7->as_VMReg());
|
|
|
|
// Output Registers 0-7
|
|
reg_def R_O0H(SOC, SOC, Op_RegI,136, O0->as_VMReg()->next());
|
|
reg_def R_O0 (SOC, SOC, Op_RegI, 8, O0->as_VMReg());
|
|
reg_def R_O1H(SOC, SOC, Op_RegI,137, O1->as_VMReg()->next());
|
|
reg_def R_O1 (SOC, SOC, Op_RegI, 9, O1->as_VMReg());
|
|
reg_def R_O2H(SOC, SOC, Op_RegI,138, O2->as_VMReg()->next());
|
|
reg_def R_O2 (SOC, SOC, Op_RegI, 10, O2->as_VMReg());
|
|
reg_def R_O3H(SOC, SOC, Op_RegI,139, O3->as_VMReg()->next());
|
|
reg_def R_O3 (SOC, SOC, Op_RegI, 11, O3->as_VMReg());
|
|
reg_def R_O4H(SOC, SOC, Op_RegI,140, O4->as_VMReg()->next());
|
|
reg_def R_O4 (SOC, SOC, Op_RegI, 12, O4->as_VMReg());
|
|
reg_def R_O5H(SOC, SOC, Op_RegI,141, O5->as_VMReg()->next());
|
|
reg_def R_O5 (SOC, SOC, Op_RegI, 13, O5->as_VMReg());
|
|
reg_def R_SPH( NS, NS, Op_RegI,142, SP->as_VMReg()->next());
|
|
reg_def R_SP ( NS, NS, Op_RegI, 14, SP->as_VMReg());
|
|
reg_def R_O7H(SOC, SOC, Op_RegI,143, O7->as_VMReg()->next());
|
|
reg_def R_O7 (SOC, SOC, Op_RegI, 15, O7->as_VMReg());
|
|
|
|
// Local Registers 0-7
|
|
reg_def R_L0H( NS, NS, Op_RegI,144, L0->as_VMReg()->next());
|
|
reg_def R_L0 ( NS, NS, Op_RegI, 16, L0->as_VMReg());
|
|
reg_def R_L1H( NS, NS, Op_RegI,145, L1->as_VMReg()->next());
|
|
reg_def R_L1 ( NS, NS, Op_RegI, 17, L1->as_VMReg());
|
|
reg_def R_L2H( NS, NS, Op_RegI,146, L2->as_VMReg()->next());
|
|
reg_def R_L2 ( NS, NS, Op_RegI, 18, L2->as_VMReg());
|
|
reg_def R_L3H( NS, NS, Op_RegI,147, L3->as_VMReg()->next());
|
|
reg_def R_L3 ( NS, NS, Op_RegI, 19, L3->as_VMReg());
|
|
reg_def R_L4H( NS, NS, Op_RegI,148, L4->as_VMReg()->next());
|
|
reg_def R_L4 ( NS, NS, Op_RegI, 20, L4->as_VMReg());
|
|
reg_def R_L5H( NS, NS, Op_RegI,149, L5->as_VMReg()->next());
|
|
reg_def R_L5 ( NS, NS, Op_RegI, 21, L5->as_VMReg());
|
|
reg_def R_L6H( NS, NS, Op_RegI,150, L6->as_VMReg()->next());
|
|
reg_def R_L6 ( NS, NS, Op_RegI, 22, L6->as_VMReg());
|
|
reg_def R_L7H( NS, NS, Op_RegI,151, L7->as_VMReg()->next());
|
|
reg_def R_L7 ( NS, NS, Op_RegI, 23, L7->as_VMReg());
|
|
|
|
// Input Registers 0-7
|
|
reg_def R_I0H( NS, NS, Op_RegI,152, I0->as_VMReg()->next());
|
|
reg_def R_I0 ( NS, NS, Op_RegI, 24, I0->as_VMReg());
|
|
reg_def R_I1H( NS, NS, Op_RegI,153, I1->as_VMReg()->next());
|
|
reg_def R_I1 ( NS, NS, Op_RegI, 25, I1->as_VMReg());
|
|
reg_def R_I2H( NS, NS, Op_RegI,154, I2->as_VMReg()->next());
|
|
reg_def R_I2 ( NS, NS, Op_RegI, 26, I2->as_VMReg());
|
|
reg_def R_I3H( NS, NS, Op_RegI,155, I3->as_VMReg()->next());
|
|
reg_def R_I3 ( NS, NS, Op_RegI, 27, I3->as_VMReg());
|
|
reg_def R_I4H( NS, NS, Op_RegI,156, I4->as_VMReg()->next());
|
|
reg_def R_I4 ( NS, NS, Op_RegI, 28, I4->as_VMReg());
|
|
reg_def R_I5H( NS, NS, Op_RegI,157, I5->as_VMReg()->next());
|
|
reg_def R_I5 ( NS, NS, Op_RegI, 29, I5->as_VMReg());
|
|
reg_def R_FPH( NS, NS, Op_RegI,158, FP->as_VMReg()->next());
|
|
reg_def R_FP ( NS, NS, Op_RegI, 30, FP->as_VMReg());
|
|
reg_def R_I7H( NS, NS, Op_RegI,159, I7->as_VMReg()->next());
|
|
reg_def R_I7 ( NS, NS, Op_RegI, 31, I7->as_VMReg());
|
|
|
|
// ----------------------------
|
|
// Float/Double Registers
|
|
// ----------------------------
|
|
|
|
// Float Registers
|
|
reg_def R_F0 ( SOC, SOC, Op_RegF, 0, F0->as_VMReg());
|
|
reg_def R_F1 ( SOC, SOC, Op_RegF, 1, F1->as_VMReg());
|
|
reg_def R_F2 ( SOC, SOC, Op_RegF, 2, F2->as_VMReg());
|
|
reg_def R_F3 ( SOC, SOC, Op_RegF, 3, F3->as_VMReg());
|
|
reg_def R_F4 ( SOC, SOC, Op_RegF, 4, F4->as_VMReg());
|
|
reg_def R_F5 ( SOC, SOC, Op_RegF, 5, F5->as_VMReg());
|
|
reg_def R_F6 ( SOC, SOC, Op_RegF, 6, F6->as_VMReg());
|
|
reg_def R_F7 ( SOC, SOC, Op_RegF, 7, F7->as_VMReg());
|
|
reg_def R_F8 ( SOC, SOC, Op_RegF, 8, F8->as_VMReg());
|
|
reg_def R_F9 ( SOC, SOC, Op_RegF, 9, F9->as_VMReg());
|
|
reg_def R_F10( SOC, SOC, Op_RegF, 10, F10->as_VMReg());
|
|
reg_def R_F11( SOC, SOC, Op_RegF, 11, F11->as_VMReg());
|
|
reg_def R_F12( SOC, SOC, Op_RegF, 12, F12->as_VMReg());
|
|
reg_def R_F13( SOC, SOC, Op_RegF, 13, F13->as_VMReg());
|
|
reg_def R_F14( SOC, SOC, Op_RegF, 14, F14->as_VMReg());
|
|
reg_def R_F15( SOC, SOC, Op_RegF, 15, F15->as_VMReg());
|
|
reg_def R_F16( SOC, SOC, Op_RegF, 16, F16->as_VMReg());
|
|
reg_def R_F17( SOC, SOC, Op_RegF, 17, F17->as_VMReg());
|
|
reg_def R_F18( SOC, SOC, Op_RegF, 18, F18->as_VMReg());
|
|
reg_def R_F19( SOC, SOC, Op_RegF, 19, F19->as_VMReg());
|
|
reg_def R_F20( SOC, SOC, Op_RegF, 20, F20->as_VMReg());
|
|
reg_def R_F21( SOC, SOC, Op_RegF, 21, F21->as_VMReg());
|
|
reg_def R_F22( SOC, SOC, Op_RegF, 22, F22->as_VMReg());
|
|
reg_def R_F23( SOC, SOC, Op_RegF, 23, F23->as_VMReg());
|
|
reg_def R_F24( SOC, SOC, Op_RegF, 24, F24->as_VMReg());
|
|
reg_def R_F25( SOC, SOC, Op_RegF, 25, F25->as_VMReg());
|
|
reg_def R_F26( SOC, SOC, Op_RegF, 26, F26->as_VMReg());
|
|
reg_def R_F27( SOC, SOC, Op_RegF, 27, F27->as_VMReg());
|
|
reg_def R_F28( SOC, SOC, Op_RegF, 28, F28->as_VMReg());
|
|
reg_def R_F29( SOC, SOC, Op_RegF, 29, F29->as_VMReg());
|
|
reg_def R_F30( SOC, SOC, Op_RegF, 30, F30->as_VMReg());
|
|
reg_def R_F31( SOC, SOC, Op_RegF, 31, F31->as_VMReg());
|
|
|
|
// Double Registers
|
|
// The rules of ADL require that double registers be defined in pairs.
|
|
// Each pair must be two 32-bit values, but not necessarily a pair of
|
|
// single float registers. In each pair, ADLC-assigned register numbers
|
|
// must be adjacent, with the lower number even. Finally, when the
|
|
// CPU stores such a register pair to memory, the word associated with
|
|
// the lower ADLC-assigned number must be stored to the lower address.
|
|
|
|
// These definitions specify the actual bit encodings of the sparc
|
|
// double fp register numbers. FloatRegisterImpl in register_sparc.hpp
|
|
// wants 0-63, so we have to convert every time we want to use fp regs
|
|
// with the macroassembler, using reg_to_DoubleFloatRegister_object().
|
|
// 255 is a flag meaning "don't go here".
|
|
// I believe we can't handle callee-save doubles D32 and up until
|
|
// the place in the sparc stack crawler that asserts on the 255 is
|
|
// fixed up.
|
|
reg_def R_D32 (SOC, SOC, Op_RegD, 1, F32->as_VMReg());
|
|
reg_def R_D32x(SOC, SOC, Op_RegD,255, F32->as_VMReg()->next());
|
|
reg_def R_D34 (SOC, SOC, Op_RegD, 3, F34->as_VMReg());
|
|
reg_def R_D34x(SOC, SOC, Op_RegD,255, F34->as_VMReg()->next());
|
|
reg_def R_D36 (SOC, SOC, Op_RegD, 5, F36->as_VMReg());
|
|
reg_def R_D36x(SOC, SOC, Op_RegD,255, F36->as_VMReg()->next());
|
|
reg_def R_D38 (SOC, SOC, Op_RegD, 7, F38->as_VMReg());
|
|
reg_def R_D38x(SOC, SOC, Op_RegD,255, F38->as_VMReg()->next());
|
|
reg_def R_D40 (SOC, SOC, Op_RegD, 9, F40->as_VMReg());
|
|
reg_def R_D40x(SOC, SOC, Op_RegD,255, F40->as_VMReg()->next());
|
|
reg_def R_D42 (SOC, SOC, Op_RegD, 11, F42->as_VMReg());
|
|
reg_def R_D42x(SOC, SOC, Op_RegD,255, F42->as_VMReg()->next());
|
|
reg_def R_D44 (SOC, SOC, Op_RegD, 13, F44->as_VMReg());
|
|
reg_def R_D44x(SOC, SOC, Op_RegD,255, F44->as_VMReg()->next());
|
|
reg_def R_D46 (SOC, SOC, Op_RegD, 15, F46->as_VMReg());
|
|
reg_def R_D46x(SOC, SOC, Op_RegD,255, F46->as_VMReg()->next());
|
|
reg_def R_D48 (SOC, SOC, Op_RegD, 17, F48->as_VMReg());
|
|
reg_def R_D48x(SOC, SOC, Op_RegD,255, F48->as_VMReg()->next());
|
|
reg_def R_D50 (SOC, SOC, Op_RegD, 19, F50->as_VMReg());
|
|
reg_def R_D50x(SOC, SOC, Op_RegD,255, F50->as_VMReg()->next());
|
|
reg_def R_D52 (SOC, SOC, Op_RegD, 21, F52->as_VMReg());
|
|
reg_def R_D52x(SOC, SOC, Op_RegD,255, F52->as_VMReg()->next());
|
|
reg_def R_D54 (SOC, SOC, Op_RegD, 23, F54->as_VMReg());
|
|
reg_def R_D54x(SOC, SOC, Op_RegD,255, F54->as_VMReg()->next());
|
|
reg_def R_D56 (SOC, SOC, Op_RegD, 25, F56->as_VMReg());
|
|
reg_def R_D56x(SOC, SOC, Op_RegD,255, F56->as_VMReg()->next());
|
|
reg_def R_D58 (SOC, SOC, Op_RegD, 27, F58->as_VMReg());
|
|
reg_def R_D58x(SOC, SOC, Op_RegD,255, F58->as_VMReg()->next());
|
|
reg_def R_D60 (SOC, SOC, Op_RegD, 29, F60->as_VMReg());
|
|
reg_def R_D60x(SOC, SOC, Op_RegD,255, F60->as_VMReg()->next());
|
|
reg_def R_D62 (SOC, SOC, Op_RegD, 31, F62->as_VMReg());
|
|
reg_def R_D62x(SOC, SOC, Op_RegD,255, F62->as_VMReg()->next());
|
|
|
|
|
|
// ----------------------------
|
|
// Special Registers
|
|
// Condition Codes Flag Registers
|
|
// I tried to break out ICC and XCC but it's not very pretty.
|
|
// Every Sparc instruction which defs/kills one also kills the other.
|
|
// Hence every compare instruction which defs one kind of flags ends
|
|
// up needing a kill of the other.
|
|
reg_def CCR (SOC, SOC, Op_RegFlags, 0, VMRegImpl::Bad());
|
|
|
|
reg_def FCC0(SOC, SOC, Op_RegFlags, 0, VMRegImpl::Bad());
|
|
reg_def FCC1(SOC, SOC, Op_RegFlags, 1, VMRegImpl::Bad());
|
|
reg_def FCC2(SOC, SOC, Op_RegFlags, 2, VMRegImpl::Bad());
|
|
reg_def FCC3(SOC, SOC, Op_RegFlags, 3, VMRegImpl::Bad());
|
|
|
|
// ----------------------------
|
|
// Specify the enum values for the registers. These enums are only used by the
|
|
// OptoReg "class". We can convert these enum values at will to VMReg when needed
|
|
// for visibility to the rest of the vm. The order of this enum influences the
|
|
// register allocator so having the freedom to set this order and not be stuck
|
|
// with the order that is natural for the rest of the vm is worth it.
|
|
alloc_class chunk0(
|
|
R_L0,R_L0H, R_L1,R_L1H, R_L2,R_L2H, R_L3,R_L3H, R_L4,R_L4H, R_L5,R_L5H, R_L6,R_L6H, R_L7,R_L7H,
|
|
R_G0,R_G0H, R_G1,R_G1H, R_G2,R_G2H, R_G3,R_G3H, R_G4,R_G4H, R_G5,R_G5H, R_G6,R_G6H, R_G7,R_G7H,
|
|
R_O7,R_O7H, R_SP,R_SPH, R_O0,R_O0H, R_O1,R_O1H, R_O2,R_O2H, R_O3,R_O3H, R_O4,R_O4H, R_O5,R_O5H,
|
|
R_I0,R_I0H, R_I1,R_I1H, R_I2,R_I2H, R_I3,R_I3H, R_I4,R_I4H, R_I5,R_I5H, R_FP,R_FPH, R_I7,R_I7H);
|
|
|
|
// Note that a register is not allocatable unless it is also mentioned
|
|
// in a widely-used reg_class below. Thus, R_G7 and R_G0 are outside i_reg.
|
|
|
|
alloc_class chunk1(
|
|
// The first registers listed here are those most likely to be used
|
|
// as temporaries. We move F0..F7 away from the front of the list,
|
|
// to reduce the likelihood of interferences with parameters and
|
|
// return values. Likewise, we avoid using F0/F1 for parameters,
|
|
// since they are used for return values.
|
|
// This FPU fine-tuning is worth about 1% on the SPEC geomean.
|
|
R_F8 ,R_F9 ,R_F10,R_F11,R_F12,R_F13,R_F14,R_F15,
|
|
R_F16,R_F17,R_F18,R_F19,R_F20,R_F21,R_F22,R_F23,
|
|
R_F24,R_F25,R_F26,R_F27,R_F28,R_F29,R_F30,R_F31,
|
|
R_F0 ,R_F1 ,R_F2 ,R_F3 ,R_F4 ,R_F5 ,R_F6 ,R_F7 , // used for arguments and return values
|
|
R_D32,R_D32x,R_D34,R_D34x,R_D36,R_D36x,R_D38,R_D38x,
|
|
R_D40,R_D40x,R_D42,R_D42x,R_D44,R_D44x,R_D46,R_D46x,
|
|
R_D48,R_D48x,R_D50,R_D50x,R_D52,R_D52x,R_D54,R_D54x,
|
|
R_D56,R_D56x,R_D58,R_D58x,R_D60,R_D60x,R_D62,R_D62x);
|
|
|
|
alloc_class chunk2(CCR, FCC0, FCC1, FCC2, FCC3);
|
|
|
|
//----------Architecture Description Register Classes--------------------------
|
|
// Several register classes are automatically defined based upon information in
|
|
// this architecture description.
|
|
// 1) reg_class inline_cache_reg ( as defined in frame section )
|
|
// 2) reg_class interpreter_method_oop_reg ( as defined in frame section )
|
|
// 3) reg_class stack_slots( /* one chunk of stack-based "registers" */ )
|
|
//
|
|
|
|
// G0 is not included in integer class since it has special meaning.
|
|
reg_class g0_reg(R_G0);
|
|
|
|
// ----------------------------
|
|
// Integer Register Classes
|
|
// ----------------------------
|
|
// Exclusions from i_reg:
|
|
// R_G0: hardwired zero
|
|
// R_G2: reserved by HotSpot to the TLS register (invariant within Java)
|
|
// R_G6: reserved by Solaris ABI to tools
|
|
// R_G7: reserved by Solaris ABI to libthread
|
|
// R_O7: Used as a temp in many encodings
|
|
reg_class int_reg(R_G1,R_G3,R_G4,R_G5,R_O0,R_O1,R_O2,R_O3,R_O4,R_O5,R_L0,R_L1,R_L2,R_L3,R_L4,R_L5,R_L6,R_L7,R_I0,R_I1,R_I2,R_I3,R_I4,R_I5);
|
|
|
|
// Class for all integer registers, except the G registers. This is used for
|
|
// encodings which use G registers as temps. The regular inputs to such
|
|
// instructions use a "notemp_" prefix, as a hack to ensure that the allocator
|
|
// will not put an input into a temp register.
|
|
reg_class notemp_int_reg(R_O0,R_O1,R_O2,R_O3,R_O4,R_O5,R_L0,R_L1,R_L2,R_L3,R_L4,R_L5,R_L6,R_L7,R_I0,R_I1,R_I2,R_I3,R_I4,R_I5);
|
|
|
|
reg_class g1_regI(R_G1);
|
|
reg_class g3_regI(R_G3);
|
|
reg_class g4_regI(R_G4);
|
|
reg_class o0_regI(R_O0);
|
|
reg_class o7_regI(R_O7);
|
|
|
|
// ----------------------------
|
|
// Pointer Register Classes
|
|
// ----------------------------
|
|
// 64-bit build means 64-bit pointers means hi/lo pairs
|
|
reg_class ptr_reg( R_G1H,R_G1, R_G3H,R_G3, R_G4H,R_G4, R_G5H,R_G5,
|
|
R_O0H,R_O0, R_O1H,R_O1, R_O2H,R_O2, R_O3H,R_O3, R_O4H,R_O4, R_O5H,R_O5,
|
|
R_L0H,R_L0, R_L1H,R_L1, R_L2H,R_L2, R_L3H,R_L3, R_L4H,R_L4, R_L5H,R_L5, R_L6H,R_L6, R_L7H,R_L7,
|
|
R_I0H,R_I0, R_I1H,R_I1, R_I2H,R_I2, R_I3H,R_I3, R_I4H,R_I4, R_I5H,R_I5 );
|
|
// Lock encodings use G3 and G4 internally
|
|
reg_class lock_ptr_reg( R_G1H,R_G1, R_G5H,R_G5,
|
|
R_O0H,R_O0, R_O1H,R_O1, R_O2H,R_O2, R_O3H,R_O3, R_O4H,R_O4, R_O5H,R_O5,
|
|
R_L0H,R_L0, R_L1H,R_L1, R_L2H,R_L2, R_L3H,R_L3, R_L4H,R_L4, R_L5H,R_L5, R_L6H,R_L6, R_L7H,R_L7,
|
|
R_I0H,R_I0, R_I1H,R_I1, R_I2H,R_I2, R_I3H,R_I3, R_I4H,R_I4, R_I5H,R_I5 );
|
|
// Special class for storeP instructions, which can store SP or RPC to TLS.
|
|
// It is also used for memory addressing, allowing direct TLS addressing.
|
|
reg_class sp_ptr_reg( R_G1H,R_G1, R_G2H,R_G2, R_G3H,R_G3, R_G4H,R_G4, R_G5H,R_G5,
|
|
R_O0H,R_O0, R_O1H,R_O1, R_O2H,R_O2, R_O3H,R_O3, R_O4H,R_O4, R_O5H,R_O5, R_SPH,R_SP,
|
|
R_L0H,R_L0, R_L1H,R_L1, R_L2H,R_L2, R_L3H,R_L3, R_L4H,R_L4, R_L5H,R_L5, R_L6H,R_L6, R_L7H,R_L7,
|
|
R_I0H,R_I0, R_I1H,R_I1, R_I2H,R_I2, R_I3H,R_I3, R_I4H,R_I4, R_I5H,R_I5, R_FPH,R_FP );
|
|
// R_L7 is the lowest-priority callee-save (i.e., NS) register
|
|
// We use it to save R_G2 across calls out of Java.
|
|
reg_class l7_regP(R_L7H,R_L7);
|
|
|
|
// Other special pointer regs
|
|
reg_class g1_regP(R_G1H,R_G1);
|
|
reg_class g2_regP(R_G2H,R_G2);
|
|
reg_class g3_regP(R_G3H,R_G3);
|
|
reg_class g4_regP(R_G4H,R_G4);
|
|
reg_class g5_regP(R_G5H,R_G5);
|
|
reg_class i0_regP(R_I0H,R_I0);
|
|
reg_class o0_regP(R_O0H,R_O0);
|
|
reg_class o1_regP(R_O1H,R_O1);
|
|
reg_class o2_regP(R_O2H,R_O2);
|
|
reg_class o7_regP(R_O7H,R_O7);
|
|
|
|
|
|
// ----------------------------
|
|
// Long Register Classes
|
|
// ----------------------------
|
|
// Longs in 1 register. Aligned adjacent hi/lo pairs.
|
|
// Note: O7 is never in this class; it is sometimes used as an encoding temp.
|
|
reg_class long_reg( R_G1H,R_G1, R_G3H,R_G3, R_G4H,R_G4, R_G5H,R_G5
|
|
,R_O0H,R_O0, R_O1H,R_O1, R_O2H,R_O2, R_O3H,R_O3, R_O4H,R_O4, R_O5H,R_O5
|
|
// 64-bit, longs in 1 register: use all 64-bit integer registers
|
|
,R_L0H,R_L0, R_L1H,R_L1, R_L2H,R_L2, R_L3H,R_L3, R_L4H,R_L4, R_L5H,R_L5, R_L6H,R_L6, R_L7H,R_L7
|
|
,R_I0H,R_I0, R_I1H,R_I1, R_I2H,R_I2, R_I3H,R_I3, R_I4H,R_I4, R_I5H,R_I5
|
|
);
|
|
|
|
reg_class g1_regL(R_G1H,R_G1);
|
|
reg_class g3_regL(R_G3H,R_G3);
|
|
reg_class o2_regL(R_O2H,R_O2);
|
|
reg_class o7_regL(R_O7H,R_O7);
|
|
|
|
// ----------------------------
|
|
// Special Class for Condition Code Flags Register
|
|
reg_class int_flags(CCR);
|
|
reg_class float_flags(FCC0,FCC1,FCC2,FCC3);
|
|
reg_class float_flag0(FCC0);
|
|
|
|
|
|
// ----------------------------
|
|
// Float Point Register Classes
|
|
// ----------------------------
|
|
// Skip F30/F31, they are reserved for mem-mem copies
|
|
reg_class sflt_reg(R_F0,R_F1,R_F2,R_F3,R_F4,R_F5,R_F6,R_F7,R_F8,R_F9,R_F10,R_F11,R_F12,R_F13,R_F14,R_F15,R_F16,R_F17,R_F18,R_F19,R_F20,R_F21,R_F22,R_F23,R_F24,R_F25,R_F26,R_F27,R_F28,R_F29);
|
|
|
|
// Paired floating point registers--they show up in the same order as the floats,
|
|
// but they are used with the "Op_RegD" type, and always occur in even/odd pairs.
|
|
reg_class dflt_reg(R_F0, R_F1, R_F2, R_F3, R_F4, R_F5, R_F6, R_F7, R_F8, R_F9, R_F10,R_F11,R_F12,R_F13,R_F14,R_F15,
|
|
R_F16,R_F17,R_F18,R_F19,R_F20,R_F21,R_F22,R_F23,R_F24,R_F25,R_F26,R_F27,R_F28,R_F29,
|
|
/* Use extra V9 double registers; this AD file does not support V8 */
|
|
R_D32,R_D32x,R_D34,R_D34x,R_D36,R_D36x,R_D38,R_D38x,R_D40,R_D40x,R_D42,R_D42x,R_D44,R_D44x,R_D46,R_D46x,
|
|
R_D48,R_D48x,R_D50,R_D50x,R_D52,R_D52x,R_D54,R_D54x,R_D56,R_D56x,R_D58,R_D58x,R_D60,R_D60x,R_D62,R_D62x
|
|
);
|
|
|
|
// Paired floating point registers--they show up in the same order as the floats,
|
|
// but they are used with the "Op_RegD" type, and always occur in even/odd pairs.
|
|
// This class is usable for mis-aligned loads as happen in I2C adapters.
|
|
reg_class dflt_low_reg(R_F0, R_F1, R_F2, R_F3, R_F4, R_F5, R_F6, R_F7, R_F8, R_F9, R_F10,R_F11,R_F12,R_F13,R_F14,R_F15,
|
|
R_F16,R_F17,R_F18,R_F19,R_F20,R_F21,R_F22,R_F23,R_F24,R_F25,R_F26,R_F27,R_F28,R_F29);
|
|
%}
|
|
|
|
//----------DEFINITION BLOCK---------------------------------------------------
|
|
// Define name --> value mappings to inform the ADLC of an integer valued name
|
|
// Current support includes integer values in the range [0, 0x7FFFFFFF]
|
|
// Format:
|
|
// int_def <name> ( <int_value>, <expression>);
|
|
// Generated Code in ad_<arch>.hpp
|
|
// #define <name> (<expression>)
|
|
// // value == <int_value>
|
|
// Generated code in ad_<arch>.cpp adlc_verification()
|
|
// assert( <name> == <int_value>, "Expect (<expression>) to equal <int_value>");
|
|
//
|
|
definitions %{
|
|
// The default cost (of an ALU instruction).
|
|
int_def DEFAULT_COST ( 100, 100);
|
|
int_def HUGE_COST (1000000, 1000000);
|
|
|
|
// Memory refs are twice as expensive as run-of-the-mill.
|
|
int_def MEMORY_REF_COST ( 200, DEFAULT_COST * 2);
|
|
|
|
// Branches are even more expensive.
|
|
int_def BRANCH_COST ( 300, DEFAULT_COST * 3);
|
|
int_def CALL_COST ( 300, DEFAULT_COST * 3);
|
|
%}
|
|
|
|
|
|
//----------SOURCE BLOCK-------------------------------------------------------
|
|
// This is a block of C++ code which provides values, functions, and
|
|
// definitions necessary in the rest of the architecture description
|
|
source_hpp %{
|
|
// Header information of the source block.
|
|
// Method declarations/definitions which are used outside
|
|
// the ad-scope can conveniently be defined here.
|
|
//
|
|
// To keep related declarations/definitions/uses close together,
|
|
// we switch between source %{ }% and source_hpp %{ }% freely as needed.
|
|
|
|
// Must be visible to the DFA in dfa_sparc.cpp
|
|
extern bool can_branch_register( Node *bol, Node *cmp );
|
|
|
|
extern bool use_block_zeroing(Node* count);
|
|
|
|
// Macros to extract hi & lo halves from a long pair.
|
|
// G0 is not part of any long pair, so assert on that.
|
|
// Prevents accidentally using G1 instead of G0.
|
|
#define LONG_HI_REG(x) (x)
|
|
#define LONG_LO_REG(x) (x)
|
|
|
|
class CallStubImpl {
|
|
|
|
//--------------------------------------------------------------
|
|
//---< Used for optimization in Compile::Shorten_branches >---
|
|
//--------------------------------------------------------------
|
|
|
|
public:
|
|
// Size of call trampoline stub.
|
|
static uint size_call_trampoline() {
|
|
return 0; // no call trampolines on this platform
|
|
}
|
|
|
|
// number of relocations needed by a call trampoline stub
|
|
static uint reloc_call_trampoline() {
|
|
return 0; // no call trampolines on this platform
|
|
}
|
|
};
|
|
|
|
class HandlerImpl {
|
|
|
|
public:
|
|
|
|
static int emit_exception_handler(CodeBuffer &cbuf);
|
|
static int emit_deopt_handler(CodeBuffer& cbuf);
|
|
|
|
static uint size_exception_handler() {
|
|
return ( NativeJump::instruction_size ); // sethi;jmp;nop
|
|
}
|
|
|
|
static uint size_deopt_handler() {
|
|
return ( 4+ NativeJump::instruction_size ); // save;sethi;jmp;restore
|
|
}
|
|
};
|
|
|
|
%}
|
|
|
|
source %{
|
|
#define __ _masm.
|
|
|
|
// tertiary op of a LoadP or StoreP encoding
|
|
#define REGP_OP true
|
|
|
|
static FloatRegister reg_to_SingleFloatRegister_object(int register_encoding);
|
|
static FloatRegister reg_to_DoubleFloatRegister_object(int register_encoding);
|
|
static Register reg_to_register_object(int register_encoding);
|
|
|
|
// Used by the DFA in dfa_sparc.cpp.
|
|
// Check for being able to use a V9 branch-on-register. Requires a
|
|
// compare-vs-zero, equal/not-equal, of a value which was zero- or sign-
|
|
// extended. Doesn't work following an integer ADD, for example, because of
|
|
// overflow (-1 incremented yields 0 plus a carry in the high-order word). On
|
|
// 32-bit V9 systems, interrupts currently blow away the high-order 32 bits and
|
|
// replace them with zero, which could become sign-extension in a different OS
|
|
// release. There's no obvious reason why an interrupt will ever fill these
|
|
// bits with non-zero junk (the registers are reloaded with standard LD
|
|
// instructions which either zero-fill or sign-fill).
|
|
bool can_branch_register( Node *bol, Node *cmp ) {
|
|
if( !BranchOnRegister ) return false;
|
|
if( cmp->Opcode() == Op_CmpP )
|
|
return true; // No problems with pointer compares
|
|
if( cmp->Opcode() == Op_CmpL )
|
|
return true; // No problems with long compares
|
|
|
|
if( !SparcV9RegsHiBitsZero ) return false;
|
|
if( bol->as_Bool()->_test._test != BoolTest::ne &&
|
|
bol->as_Bool()->_test._test != BoolTest::eq )
|
|
return false;
|
|
|
|
// Check for comparing against a 'safe' value. Any operation which
|
|
// clears out the high word is safe. Thus, loads and certain shifts
|
|
// are safe, as are non-negative constants. Any operation which
|
|
// preserves zero bits in the high word is safe as long as each of its
|
|
// inputs are safe. Thus, phis and bitwise booleans are safe if their
|
|
// inputs are safe. At present, the only important case to recognize
|
|
// seems to be loads. Constants should fold away, and shifts &
|
|
// logicals can use the 'cc' forms.
|
|
Node *x = cmp->in(1);
|
|
if( x->is_Load() ) return true;
|
|
if( x->is_Phi() ) {
|
|
for( uint i = 1; i < x->req(); i++ )
|
|
if( !x->in(i)->is_Load() )
|
|
return false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool use_block_zeroing(Node* count) {
|
|
// Use BIS for zeroing if count is not constant
|
|
// or it is >= BlockZeroingLowLimit.
|
|
return UseBlockZeroing && (count->find_intptr_t_con(BlockZeroingLowLimit) >= BlockZeroingLowLimit);
|
|
}
|
|
|
|
// ****************************************************************************
|
|
|
|
// REQUIRED FUNCTIONALITY
|
|
|
|
// !!!!! Special hack to get all type of calls to specify the byte offset
|
|
// from the start of the call to the point where the return address
|
|
// will point.
|
|
// The "return address" is the address of the call instruction, plus 8.
|
|
|
|
int MachCallStaticJavaNode::ret_addr_offset() {
|
|
int offset = NativeCall::instruction_size; // call; delay slot
|
|
if (_method_handle_invoke)
|
|
offset += 4; // restore SP
|
|
return offset;
|
|
}
|
|
|
|
int MachCallDynamicJavaNode::ret_addr_offset() {
|
|
int vtable_index = this->_vtable_index;
|
|
if (vtable_index < 0) {
|
|
// must be invalid_vtable_index, not nonvirtual_vtable_index
|
|
assert(vtable_index == Method::invalid_vtable_index, "correct sentinel value");
|
|
return (NativeMovConstReg::instruction_size +
|
|
NativeCall::instruction_size); // sethi; setlo; call; delay slot
|
|
} else {
|
|
assert(!UseInlineCaches, "expect vtable calls only if not using ICs");
|
|
int entry_offset = in_bytes(Klass::vtable_start_offset()) + vtable_index*vtableEntry::size_in_bytes();
|
|
int v_off = entry_offset + vtableEntry::method_offset_in_bytes();
|
|
int klass_load_size;
|
|
if (UseCompressedClassPointers) {
|
|
assert(Universe::heap() != NULL, "java heap should be initialized");
|
|
klass_load_size = MacroAssembler::instr_size_for_decode_klass_not_null() + 1*BytesPerInstWord;
|
|
} else {
|
|
klass_load_size = 1*BytesPerInstWord;
|
|
}
|
|
if (Assembler::is_simm13(v_off)) {
|
|
return klass_load_size +
|
|
(2*BytesPerInstWord + // ld_ptr, ld_ptr
|
|
NativeCall::instruction_size); // call; delay slot
|
|
} else {
|
|
return klass_load_size +
|
|
(4*BytesPerInstWord + // set_hi, set, ld_ptr, ld_ptr
|
|
NativeCall::instruction_size); // call; delay slot
|
|
}
|
|
}
|
|
}
|
|
|
|
int MachCallRuntimeNode::ret_addr_offset() {
|
|
if (MacroAssembler::is_far_target(entry_point())) {
|
|
return NativeFarCall::instruction_size;
|
|
} else {
|
|
return NativeCall::instruction_size;
|
|
}
|
|
}
|
|
|
|
// Indicate if the safepoint node needs the polling page as an input.
|
|
// Since Sparc does not have absolute addressing, it does.
|
|
bool SafePointNode::needs_polling_address_input() {
|
|
return true;
|
|
}
|
|
|
|
// emit an interrupt that is caught by the debugger (for debugging compiler)
|
|
void emit_break(CodeBuffer &cbuf) {
|
|
MacroAssembler _masm(&cbuf);
|
|
__ breakpoint_trap();
|
|
}
|
|
|
|
#ifndef PRODUCT
|
|
void MachBreakpointNode::format( PhaseRegAlloc *, outputStream *st ) const {
|
|
st->print("TA");
|
|
}
|
|
#endif
|
|
|
|
void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
|
|
emit_break(cbuf);
|
|
}
|
|
|
|
uint MachBreakpointNode::size(PhaseRegAlloc *ra_) const {
|
|
return MachNode::size(ra_);
|
|
}
|
|
|
|
// Traceable jump
|
|
void emit_jmpl(CodeBuffer &cbuf, int jump_target) {
|
|
MacroAssembler _masm(&cbuf);
|
|
Register rdest = reg_to_register_object(jump_target);
|
|
__ JMP(rdest, 0);
|
|
__ delayed()->nop();
|
|
}
|
|
|
|
// Traceable jump and set exception pc
|
|
void emit_jmpl_set_exception_pc(CodeBuffer &cbuf, int jump_target) {
|
|
MacroAssembler _masm(&cbuf);
|
|
Register rdest = reg_to_register_object(jump_target);
|
|
__ JMP(rdest, 0);
|
|
__ delayed()->add(O7, frame::pc_return_offset, Oissuing_pc );
|
|
}
|
|
|
|
void emit_nop(CodeBuffer &cbuf) {
|
|
MacroAssembler _masm(&cbuf);
|
|
__ nop();
|
|
}
|
|
|
|
void emit_illtrap(CodeBuffer &cbuf) {
|
|
MacroAssembler _masm(&cbuf);
|
|
__ illtrap(0);
|
|
}
|
|
|
|
|
|
intptr_t get_offset_from_base(const MachNode* n, const TypePtr* atype, int disp32) {
|
|
assert(n->rule() != loadUB_rule, "");
|
|
|
|
intptr_t offset = 0;
|
|
const TypePtr *adr_type = TYPE_PTR_SENTINAL; // Check for base==RegI, disp==immP
|
|
const Node* addr = n->get_base_and_disp(offset, adr_type);
|
|
assert(adr_type == (const TypePtr*)-1, "VerifyOops: no support for sparc operands with base==RegI, disp==immP");
|
|
assert(addr != NULL && addr != (Node*)-1, "invalid addr");
|
|
assert(addr->bottom_type()->isa_oopptr() == atype, "");
|
|
atype = atype->add_offset(offset);
|
|
assert(disp32 == offset, "wrong disp32");
|
|
return atype->_offset;
|
|
}
|
|
|
|
|
|
intptr_t get_offset_from_base_2(const MachNode* n, const TypePtr* atype, int disp32) {
|
|
assert(n->rule() != loadUB_rule, "");
|
|
|
|
intptr_t offset = 0;
|
|
Node* addr = n->in(2);
|
|
assert(addr->bottom_type()->isa_oopptr() == atype, "");
|
|
if (addr->is_Mach() && addr->as_Mach()->ideal_Opcode() == Op_AddP) {
|
|
Node* a = addr->in(2/*AddPNode::Address*/);
|
|
Node* o = addr->in(3/*AddPNode::Offset*/);
|
|
offset = o->is_Con() ? o->bottom_type()->is_intptr_t()->get_con() : Type::OffsetBot;
|
|
atype = a->bottom_type()->is_ptr()->add_offset(offset);
|
|
assert(atype->isa_oop_ptr(), "still an oop");
|
|
}
|
|
offset = atype->is_ptr()->_offset;
|
|
if (offset != Type::OffsetBot) offset += disp32;
|
|
return offset;
|
|
}
|
|
|
|
static inline jlong replicate_immI(int con, int count, int width) {
|
|
// Load a constant replicated "count" times with width "width"
|
|
assert(count*width == 8 && width <= 4, "sanity");
|
|
int bit_width = width * 8;
|
|
jlong val = con;
|
|
val &= (((jlong) 1) << bit_width) - 1; // mask off sign bits
|
|
for (int i = 0; i < count - 1; i++) {
|
|
val |= (val << bit_width);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static inline jlong replicate_immF(float con) {
|
|
// Replicate float con 2 times and pack into vector.
|
|
int val = *((int*)&con);
|
|
jlong lval = val;
|
|
lval = (lval << 32) | (lval & 0xFFFFFFFFl);
|
|
return lval;
|
|
}
|
|
|
|
// Standard Sparc opcode form2 field breakdown
|
|
static inline void emit2_19(CodeBuffer &cbuf, int f30, int f29, int f25, int f22, int f20, int f19, int f0 ) {
|
|
f0 &= (1<<19)-1; // Mask displacement to 19 bits
|
|
int op = (f30 << 30) |
|
|
(f29 << 29) |
|
|
(f25 << 25) |
|
|
(f22 << 22) |
|
|
(f20 << 20) |
|
|
(f19 << 19) |
|
|
(f0 << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
}
|
|
|
|
// Standard Sparc opcode form2 field breakdown
|
|
static inline void emit2_22(CodeBuffer &cbuf, int f30, int f25, int f22, int f0 ) {
|
|
f0 >>= 10; // Drop 10 bits
|
|
f0 &= (1<<22)-1; // Mask displacement to 22 bits
|
|
int op = (f30 << 30) |
|
|
(f25 << 25) |
|
|
(f22 << 22) |
|
|
(f0 << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
}
|
|
|
|
// Standard Sparc opcode form3 field breakdown
|
|
static inline void emit3(CodeBuffer &cbuf, int f30, int f25, int f19, int f14, int f5, int f0 ) {
|
|
int op = (f30 << 30) |
|
|
(f25 << 25) |
|
|
(f19 << 19) |
|
|
(f14 << 14) |
|
|
(f5 << 5) |
|
|
(f0 << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
}
|
|
|
|
// Standard Sparc opcode form3 field breakdown
|
|
static inline void emit3_simm13(CodeBuffer &cbuf, int f30, int f25, int f19, int f14, int simm13 ) {
|
|
simm13 &= (1<<13)-1; // Mask to 13 bits
|
|
int op = (f30 << 30) |
|
|
(f25 << 25) |
|
|
(f19 << 19) |
|
|
(f14 << 14) |
|
|
(1 << 13) | // bit to indicate immediate-mode
|
|
(simm13<<0);
|
|
cbuf.insts()->emit_int32(op);
|
|
}
|
|
|
|
static inline void emit3_simm10(CodeBuffer &cbuf, int f30, int f25, int f19, int f14, int simm10 ) {
|
|
simm10 &= (1<<10)-1; // Mask to 10 bits
|
|
emit3_simm13(cbuf,f30,f25,f19,f14,simm10);
|
|
}
|
|
|
|
#ifdef ASSERT
|
|
// Helper function for VerifyOops in emit_form3_mem_reg
|
|
void verify_oops_warning(const MachNode *n, int ideal_op, int mem_op) {
|
|
warning("VerifyOops encountered unexpected instruction:");
|
|
n->dump(2);
|
|
warning("Instruction has ideal_Opcode==Op_%s and op_ld==Op_%s \n", NodeClassNames[ideal_op], NodeClassNames[mem_op]);
|
|
}
|
|
#endif
|
|
|
|
|
|
void emit_form3_mem_reg(CodeBuffer &cbuf, PhaseRegAlloc* ra, const MachNode* n, int primary, int tertiary,
|
|
int src1_enc, int disp32, int src2_enc, int dst_enc) {
|
|
|
|
#ifdef ASSERT
|
|
// The following code implements the +VerifyOops feature.
|
|
// It verifies oop values which are loaded into or stored out of
|
|
// the current method activation. +VerifyOops complements techniques
|
|
// like ScavengeALot, because it eagerly inspects oops in transit,
|
|
// as they enter or leave the stack, as opposed to ScavengeALot,
|
|
// which inspects oops "at rest", in the stack or heap, at safepoints.
|
|
// For this reason, +VerifyOops can sometimes detect bugs very close
|
|
// to their point of creation. It can also serve as a cross-check
|
|
// on the validity of oop maps, when used toegether with ScavengeALot.
|
|
|
|
// It would be good to verify oops at other points, especially
|
|
// when an oop is used as a base pointer for a load or store.
|
|
// This is presently difficult, because it is hard to know when
|
|
// a base address is biased or not. (If we had such information,
|
|
// it would be easy and useful to make a two-argument version of
|
|
// verify_oop which unbiases the base, and performs verification.)
|
|
|
|
assert((uint)tertiary == 0xFFFFFFFF || tertiary == REGP_OP, "valid tertiary");
|
|
bool is_verified_oop_base = false;
|
|
bool is_verified_oop_load = false;
|
|
bool is_verified_oop_store = false;
|
|
int tmp_enc = -1;
|
|
if (VerifyOops && src1_enc != R_SP_enc) {
|
|
// classify the op, mainly for an assert check
|
|
int st_op = 0, ld_op = 0;
|
|
switch (primary) {
|
|
case Assembler::stb_op3: st_op = Op_StoreB; break;
|
|
case Assembler::sth_op3: st_op = Op_StoreC; break;
|
|
case Assembler::stx_op3: // may become StoreP or stay StoreI or StoreD0
|
|
case Assembler::stw_op3: st_op = Op_StoreI; break;
|
|
case Assembler::std_op3: st_op = Op_StoreL; break;
|
|
case Assembler::stf_op3: st_op = Op_StoreF; break;
|
|
case Assembler::stdf_op3: st_op = Op_StoreD; break;
|
|
|
|
case Assembler::ldsb_op3: ld_op = Op_LoadB; break;
|
|
case Assembler::ldub_op3: ld_op = Op_LoadUB; break;
|
|
case Assembler::lduh_op3: ld_op = Op_LoadUS; break;
|
|
case Assembler::ldsh_op3: ld_op = Op_LoadS; break;
|
|
case Assembler::ldx_op3: // may become LoadP or stay LoadI
|
|
case Assembler::ldsw_op3: // may become LoadP or stay LoadI
|
|
case Assembler::lduw_op3: ld_op = Op_LoadI; break;
|
|
case Assembler::ldd_op3: ld_op = Op_LoadL; break;
|
|
case Assembler::ldf_op3: ld_op = Op_LoadF; break;
|
|
case Assembler::lddf_op3: ld_op = Op_LoadD; break;
|
|
case Assembler::prefetch_op3: ld_op = Op_LoadI; break;
|
|
|
|
default: ShouldNotReachHere();
|
|
}
|
|
if (tertiary == REGP_OP) {
|
|
if (st_op == Op_StoreI) st_op = Op_StoreP;
|
|
else if (ld_op == Op_LoadI) ld_op = Op_LoadP;
|
|
else ShouldNotReachHere();
|
|
if (st_op) {
|
|
// a store
|
|
// inputs are (0:control, 1:memory, 2:address, 3:value)
|
|
Node* n2 = n->in(3);
|
|
if (n2 != NULL) {
|
|
const Type* t = n2->bottom_type();
|
|
is_verified_oop_store = t->isa_oop_ptr() ? (t->is_ptr()->_offset==0) : false;
|
|
}
|
|
} else {
|
|
// a load
|
|
const Type* t = n->bottom_type();
|
|
is_verified_oop_load = t->isa_oop_ptr() ? (t->is_ptr()->_offset==0) : false;
|
|
}
|
|
}
|
|
|
|
if (ld_op) {
|
|
// a Load
|
|
// inputs are (0:control, 1:memory, 2:address)
|
|
if (!(n->ideal_Opcode()==ld_op) && // Following are special cases
|
|
!(n->ideal_Opcode()==Op_LoadPLocked && ld_op==Op_LoadP) &&
|
|
!(n->ideal_Opcode()==Op_LoadI && ld_op==Op_LoadF) &&
|
|
!(n->ideal_Opcode()==Op_LoadF && ld_op==Op_LoadI) &&
|
|
!(n->ideal_Opcode()==Op_LoadRange && ld_op==Op_LoadI) &&
|
|
!(n->ideal_Opcode()==Op_LoadKlass && ld_op==Op_LoadP) &&
|
|
!(n->ideal_Opcode()==Op_LoadL && ld_op==Op_LoadI) &&
|
|
!(n->ideal_Opcode()==Op_LoadL_unaligned && ld_op==Op_LoadI) &&
|
|
!(n->ideal_Opcode()==Op_LoadD_unaligned && ld_op==Op_LoadF) &&
|
|
!(n->ideal_Opcode()==Op_ConvI2F && ld_op==Op_LoadF) &&
|
|
!(n->ideal_Opcode()==Op_ConvI2D && ld_op==Op_LoadF) &&
|
|
!(n->ideal_Opcode()==Op_PrefetchAllocation && ld_op==Op_LoadI) &&
|
|
!(n->ideal_Opcode()==Op_LoadVector && ld_op==Op_LoadD) &&
|
|
!(n->rule() == loadUB_rule)) {
|
|
verify_oops_warning(n, n->ideal_Opcode(), ld_op);
|
|
}
|
|
} else if (st_op) {
|
|
// a Store
|
|
// inputs are (0:control, 1:memory, 2:address, 3:value)
|
|
if (!(n->ideal_Opcode()==st_op) && // Following are special cases
|
|
!(n->ideal_Opcode()==Op_StoreCM && st_op==Op_StoreB) &&
|
|
!(n->ideal_Opcode()==Op_StoreI && st_op==Op_StoreF) &&
|
|
!(n->ideal_Opcode()==Op_StoreF && st_op==Op_StoreI) &&
|
|
!(n->ideal_Opcode()==Op_StoreL && st_op==Op_StoreI) &&
|
|
!(n->ideal_Opcode()==Op_StoreVector && st_op==Op_StoreD) &&
|
|
!(n->ideal_Opcode()==Op_StoreD && st_op==Op_StoreI && n->rule() == storeD0_rule)) {
|
|
verify_oops_warning(n, n->ideal_Opcode(), st_op);
|
|
}
|
|
}
|
|
|
|
if (src2_enc == R_G0_enc && n->rule() != loadUB_rule && n->ideal_Opcode() != Op_StoreCM ) {
|
|
Node* addr = n->in(2);
|
|
if (!(addr->is_Mach() && addr->as_Mach()->ideal_Opcode() == Op_AddP)) {
|
|
const TypeOopPtr* atype = addr->bottom_type()->isa_instptr(); // %%% oopptr?
|
|
if (atype != NULL) {
|
|
intptr_t offset = get_offset_from_base(n, atype, disp32);
|
|
intptr_t offset_2 = get_offset_from_base_2(n, atype, disp32);
|
|
if (offset != offset_2) {
|
|
get_offset_from_base(n, atype, disp32);
|
|
get_offset_from_base_2(n, atype, disp32);
|
|
}
|
|
assert(offset == offset_2, "different offsets");
|
|
if (offset == disp32) {
|
|
// we now know that src1 is a true oop pointer
|
|
is_verified_oop_base = true;
|
|
if (ld_op && src1_enc == dst_enc && ld_op != Op_LoadF && ld_op != Op_LoadD) {
|
|
if( primary == Assembler::ldd_op3 ) {
|
|
is_verified_oop_base = false; // Cannot 'ldd' into O7
|
|
} else {
|
|
tmp_enc = dst_enc;
|
|
dst_enc = R_O7_enc; // Load into O7; preserve source oop
|
|
assert(src1_enc != dst_enc, "");
|
|
}
|
|
}
|
|
}
|
|
if (st_op && (( offset == oopDesc::klass_offset_in_bytes())
|
|
|| offset == oopDesc::mark_offset_in_bytes())) {
|
|
// loading the mark should not be allowed either, but
|
|
// we don't check this since it conflicts with InlineObjectHash
|
|
// usage of LoadINode to get the mark. We could keep the
|
|
// check if we create a new LoadMarkNode
|
|
// but do not verify the object before its header is initialized
|
|
ShouldNotReachHere();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
uint instr = (Assembler::ldst_op << 30)
|
|
| (dst_enc << 25)
|
|
| (primary << 19)
|
|
| (src1_enc << 14);
|
|
|
|
uint index = src2_enc;
|
|
int disp = disp32;
|
|
|
|
if (src1_enc == R_SP_enc || src1_enc == R_FP_enc) {
|
|
disp += STACK_BIAS;
|
|
// Check that stack offset fits, load into O7 if not
|
|
if (!Assembler::is_simm13(disp)) {
|
|
MacroAssembler _masm(&cbuf);
|
|
__ set(disp, O7);
|
|
if (index != R_G0_enc) {
|
|
__ add(O7, reg_to_register_object(index), O7);
|
|
}
|
|
index = R_O7_enc;
|
|
disp = 0;
|
|
}
|
|
}
|
|
|
|
if( disp == 0 ) {
|
|
// use reg-reg form
|
|
// bit 13 is already zero
|
|
instr |= index;
|
|
} else {
|
|
// use reg-imm form
|
|
instr |= 0x00002000; // set bit 13 to one
|
|
instr |= disp & 0x1FFF;
|
|
}
|
|
|
|
cbuf.insts()->emit_int32(instr);
|
|
|
|
#ifdef ASSERT
|
|
if (VerifyOops) {
|
|
MacroAssembler _masm(&cbuf);
|
|
if (is_verified_oop_base) {
|
|
__ verify_oop(reg_to_register_object(src1_enc));
|
|
}
|
|
if (is_verified_oop_store) {
|
|
__ verify_oop(reg_to_register_object(dst_enc));
|
|
}
|
|
if (tmp_enc != -1) {
|
|
__ mov(O7, reg_to_register_object(tmp_enc));
|
|
}
|
|
if (is_verified_oop_load) {
|
|
__ verify_oop(reg_to_register_object(dst_enc));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void emit_call_reloc(CodeBuffer &cbuf, intptr_t entry_point, RelocationHolder const& rspec, bool preserve_g2 = false) {
|
|
// The method which records debug information at every safepoint
|
|
// expects the call to be the first instruction in the snippet as
|
|
// it creates a PcDesc structure which tracks the offset of a call
|
|
// from the start of the codeBlob. This offset is computed as
|
|
// code_end() - code_begin() of the code which has been emitted
|
|
// so far.
|
|
// In this particular case we have skirted around the problem by
|
|
// putting the "mov" instruction in the delay slot but the problem
|
|
// may bite us again at some other point and a cleaner/generic
|
|
// solution using relocations would be needed.
|
|
MacroAssembler _masm(&cbuf);
|
|
__ set_inst_mark();
|
|
|
|
// We flush the current window just so that there is a valid stack copy
|
|
// the fact that the current window becomes active again instantly is
|
|
// not a problem there is nothing live in it.
|
|
|
|
#ifdef ASSERT
|
|
int startpos = __ offset();
|
|
#endif /* ASSERT */
|
|
|
|
__ call((address)entry_point, rspec);
|
|
|
|
if (preserve_g2) __ delayed()->mov(G2, L7);
|
|
else __ delayed()->nop();
|
|
|
|
if (preserve_g2) __ mov(L7, G2);
|
|
|
|
#ifdef ASSERT
|
|
if (preserve_g2 && (VerifyCompiledCode || VerifyOops)) {
|
|
// Trash argument dump slots.
|
|
__ set(0xb0b8ac0db0b8ac0d, G1);
|
|
__ mov(G1, G5);
|
|
__ stx(G1, SP, STACK_BIAS + 0x80);
|
|
__ stx(G1, SP, STACK_BIAS + 0x88);
|
|
__ stx(G1, SP, STACK_BIAS + 0x90);
|
|
__ stx(G1, SP, STACK_BIAS + 0x98);
|
|
__ stx(G1, SP, STACK_BIAS + 0xA0);
|
|
__ stx(G1, SP, STACK_BIAS + 0xA8);
|
|
}
|
|
#endif /*ASSERT*/
|
|
}
|
|
|
|
//=============================================================================
|
|
// REQUIRED FUNCTIONALITY for encoding
|
|
void emit_lo(CodeBuffer &cbuf, int val) { }
|
|
void emit_hi(CodeBuffer &cbuf, int val) { }
|
|
|
|
|
|
//=============================================================================
|
|
const RegMask& MachConstantBaseNode::_out_RegMask = PTR_REG_mask();
|
|
|
|
int Compile::ConstantTable::calculate_table_base_offset() const {
|
|
if (UseRDPCForConstantTableBase) {
|
|
// The table base offset might be less but then it fits into
|
|
// simm13 anyway and we are good (cf. MachConstantBaseNode::emit).
|
|
return Assembler::min_simm13();
|
|
} else {
|
|
int offset = -(size() / 2);
|
|
if (!Assembler::is_simm13(offset)) {
|
|
offset = Assembler::min_simm13();
|
|
}
|
|
return offset;
|
|
}
|
|
}
|
|
|
|
bool MachConstantBaseNode::requires_postalloc_expand() const { return false; }
|
|
void MachConstantBaseNode::postalloc_expand(GrowableArray <Node *> *nodes, PhaseRegAlloc *ra_) {
|
|
ShouldNotReachHere();
|
|
}
|
|
|
|
void MachConstantBaseNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const {
|
|
Compile* C = ra_->C;
|
|
Compile::ConstantTable& constant_table = C->constant_table();
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
Register r = as_Register(ra_->get_encode(this));
|
|
CodeSection* consts_section = __ code()->consts();
|
|
int consts_size = consts_section->align_at_start(consts_section->size());
|
|
assert(constant_table.size() == consts_size, "must be: %d == %d", constant_table.size(), consts_size);
|
|
|
|
if (UseRDPCForConstantTableBase) {
|
|
// For the following RDPC logic to work correctly the consts
|
|
// section must be allocated right before the insts section. This
|
|
// assert checks for that. The layout and the SECT_* constants
|
|
// are defined in src/share/vm/asm/codeBuffer.hpp.
|
|
assert(CodeBuffer::SECT_CONSTS + 1 == CodeBuffer::SECT_INSTS, "must be");
|
|
int insts_offset = __ offset();
|
|
|
|
// Layout:
|
|
//
|
|
// |----------- consts section ------------|----------- insts section -----------...
|
|
// |------ constant table -----|- padding -|------------------x----
|
|
// \ current PC (RDPC instruction)
|
|
// |<------------- consts_size ----------->|<- insts_offset ->|
|
|
// \ table base
|
|
// The table base offset is later added to the load displacement
|
|
// so it has to be negative.
|
|
int table_base_offset = -(consts_size + insts_offset);
|
|
int disp;
|
|
|
|
// If the displacement from the current PC to the constant table
|
|
// base fits into simm13 we set the constant table base to the
|
|
// current PC.
|
|
if (Assembler::is_simm13(table_base_offset)) {
|
|
constant_table.set_table_base_offset(table_base_offset);
|
|
disp = 0;
|
|
} else {
|
|
// Otherwise we set the constant table base offset to the
|
|
// maximum negative displacement of load instructions to keep
|
|
// the disp as small as possible:
|
|
//
|
|
// |<------------- consts_size ----------->|<- insts_offset ->|
|
|
// |<--------- min_simm13 --------->|<-------- disp --------->|
|
|
// \ table base
|
|
table_base_offset = Assembler::min_simm13();
|
|
constant_table.set_table_base_offset(table_base_offset);
|
|
disp = (consts_size + insts_offset) + table_base_offset;
|
|
}
|
|
|
|
__ rdpc(r);
|
|
|
|
if (disp == 0) {
|
|
// Emitting an additional 'nop' instruction in order not to cause a code
|
|
// size adjustment in the code following the table setup (if the instruction
|
|
// immediately following after this section is a CTI).
|
|
__ nop();
|
|
}
|
|
else {
|
|
assert(r != O7, "need temporary");
|
|
__ sub(r, __ ensure_simm13_or_reg(disp, O7), r);
|
|
}
|
|
}
|
|
else {
|
|
// Materialize the constant table base.
|
|
address baseaddr = consts_section->start() + -(constant_table.table_base_offset());
|
|
RelocationHolder rspec = internal_word_Relocation::spec(baseaddr);
|
|
AddressLiteral base(baseaddr, rspec);
|
|
__ set(base, r);
|
|
}
|
|
}
|
|
|
|
uint MachConstantBaseNode::size(PhaseRegAlloc*) const {
|
|
if (UseRDPCForConstantTableBase) {
|
|
// This is really the worst case but generally it's only 1 instruction.
|
|
return (1 /*rdpc*/ + 1 /*sub*/ + MacroAssembler::worst_case_insts_for_set()) * BytesPerInstWord;
|
|
} else {
|
|
return MacroAssembler::worst_case_insts_for_set() * BytesPerInstWord;
|
|
}
|
|
}
|
|
|
|
#ifndef PRODUCT
|
|
void MachConstantBaseNode::format(PhaseRegAlloc* ra_, outputStream* st) const {
|
|
char reg[128];
|
|
ra_->dump_register(this, reg);
|
|
if (UseRDPCForConstantTableBase) {
|
|
st->print("RDPC %s\t! constant table base", reg);
|
|
} else {
|
|
st->print("SET &constanttable,%s\t! constant table base", reg);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//=============================================================================
|
|
|
|
#ifndef PRODUCT
|
|
void MachPrologNode::format( PhaseRegAlloc *ra_, outputStream *st ) const {
|
|
Compile* C = ra_->C;
|
|
|
|
for (int i = 0; i < OptoPrologueNops; i++) {
|
|
st->print_cr("NOP"); st->print("\t");
|
|
}
|
|
|
|
if( VerifyThread ) {
|
|
st->print_cr("Verify_Thread"); st->print("\t");
|
|
}
|
|
|
|
size_t framesize = C->frame_size_in_bytes();
|
|
int bangsize = C->bang_size_in_bytes();
|
|
|
|
// Calls to C2R adapters often do not accept exceptional returns.
|
|
// We require that their callers must bang for them. But be careful, because
|
|
// some VM calls (such as call site linkage) can use several kilobytes of
|
|
// stack. But the stack safety zone should account for that.
|
|
// See bugs 4446381, 4468289, 4497237.
|
|
if (C->need_stack_bang(bangsize)) {
|
|
st->print_cr("! stack bang (%d bytes)", bangsize); st->print("\t");
|
|
}
|
|
|
|
if (Assembler::is_simm13(-framesize)) {
|
|
st->print ("SAVE R_SP,-" SIZE_FORMAT ",R_SP",framesize);
|
|
} else {
|
|
st->print_cr("SETHI R_SP,hi%%(-" SIZE_FORMAT "),R_G3",framesize); st->print("\t");
|
|
st->print_cr("ADD R_G3,lo%%(-" SIZE_FORMAT "),R_G3",framesize); st->print("\t");
|
|
st->print ("SAVE R_SP,R_G3,R_SP");
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
|
|
Compile* C = ra_->C;
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
for (int i = 0; i < OptoPrologueNops; i++) {
|
|
__ nop();
|
|
}
|
|
|
|
__ verify_thread();
|
|
|
|
size_t framesize = C->frame_size_in_bytes();
|
|
assert(framesize >= 16*wordSize, "must have room for reg. save area");
|
|
assert(framesize%(2*wordSize) == 0, "must preserve 2*wordSize alignment");
|
|
int bangsize = C->bang_size_in_bytes();
|
|
|
|
// Calls to C2R adapters often do not accept exceptional returns.
|
|
// We require that their callers must bang for them. But be careful, because
|
|
// some VM calls (such as call site linkage) can use several kilobytes of
|
|
// stack. But the stack safety zone should account for that.
|
|
// See bugs 4446381, 4468289, 4497237.
|
|
if (C->need_stack_bang(bangsize)) {
|
|
__ generate_stack_overflow_check(bangsize);
|
|
}
|
|
|
|
if (Assembler::is_simm13(-framesize)) {
|
|
__ save(SP, -framesize, SP);
|
|
} else {
|
|
__ sethi(-framesize & ~0x3ff, G3);
|
|
__ add(G3, -framesize & 0x3ff, G3);
|
|
__ save(SP, G3, SP);
|
|
}
|
|
C->set_frame_complete( __ offset() );
|
|
|
|
if (!UseRDPCForConstantTableBase && C->has_mach_constant_base_node()) {
|
|
// NOTE: We set the table base offset here because users might be
|
|
// emitted before MachConstantBaseNode.
|
|
Compile::ConstantTable& constant_table = C->constant_table();
|
|
constant_table.set_table_base_offset(constant_table.calculate_table_base_offset());
|
|
}
|
|
}
|
|
|
|
uint MachPrologNode::size(PhaseRegAlloc *ra_) const {
|
|
return MachNode::size(ra_);
|
|
}
|
|
|
|
int MachPrologNode::reloc() const {
|
|
return 10; // a large enough number
|
|
}
|
|
|
|
//=============================================================================
|
|
#ifndef PRODUCT
|
|
void MachEpilogNode::format( PhaseRegAlloc *ra_, outputStream *st ) const {
|
|
Compile* C = ra_->C;
|
|
|
|
if(do_polling() && ra_->C->is_method_compilation()) {
|
|
if (SafepointMechanism::uses_global_page_poll()) {
|
|
st->print("SETHI #PollAddr,L0\t! Load Polling address\n\t");
|
|
} else {
|
|
st->print("LDX [R_G2 + #poll_offset],L0\t! Load local polling address\n\t");
|
|
}
|
|
st->print("LDX [L0],G0\t!Poll for Safepointing\n\t");
|
|
}
|
|
|
|
if(do_polling()) {
|
|
if (UseCBCond && !ra_->C->is_method_compilation()) {
|
|
st->print("NOP\n\t");
|
|
}
|
|
st->print("RET\n\t");
|
|
}
|
|
|
|
st->print("RESTORE");
|
|
}
|
|
#endif
|
|
|
|
void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
|
|
MacroAssembler _masm(&cbuf);
|
|
Compile* C = ra_->C;
|
|
|
|
__ verify_thread();
|
|
|
|
if (StackReservedPages > 0 && C->has_reserved_stack_access()) {
|
|
__ reserved_stack_check();
|
|
}
|
|
|
|
// If this does safepoint polling, then do it here
|
|
if(do_polling() && ra_->C->is_method_compilation()) {
|
|
if (SafepointMechanism::uses_thread_local_poll()) {
|
|
__ ld_ptr(Address(G2_thread, Thread::polling_page_offset()), L0);
|
|
} else {
|
|
AddressLiteral polling_page(os::get_polling_page());
|
|
__ sethi(polling_page, L0);
|
|
}
|
|
__ relocate(relocInfo::poll_return_type);
|
|
__ ld_ptr(L0, 0, G0);
|
|
}
|
|
|
|
// If this is a return, then stuff the restore in the delay slot
|
|
if(do_polling()) {
|
|
if (UseCBCond && !ra_->C->is_method_compilation()) {
|
|
// Insert extra padding for the case when the epilogue is preceded by
|
|
// a cbcond jump, which can't be followed by a CTI instruction
|
|
__ nop();
|
|
}
|
|
__ ret();
|
|
__ delayed()->restore();
|
|
} else {
|
|
__ restore();
|
|
}
|
|
}
|
|
|
|
uint MachEpilogNode::size(PhaseRegAlloc *ra_) const {
|
|
return MachNode::size(ra_);
|
|
}
|
|
|
|
int MachEpilogNode::reloc() const {
|
|
return 16; // a large enough number
|
|
}
|
|
|
|
const Pipeline * MachEpilogNode::pipeline() const {
|
|
return MachNode::pipeline_class();
|
|
}
|
|
|
|
int MachEpilogNode::safepoint_offset() const {
|
|
assert(SafepointMechanism::uses_global_page_poll(), "sanity");
|
|
assert( do_polling(), "no return for this epilog node");
|
|
return MacroAssembler::insts_for_sethi(os::get_polling_page()) * BytesPerInstWord;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
// Figure out which register class each belongs in: rc_int, rc_float, rc_stack
|
|
enum RC { rc_bad, rc_int, rc_float, rc_stack };
|
|
static enum RC rc_class( OptoReg::Name reg ) {
|
|
if (!OptoReg::is_valid(reg)) return rc_bad;
|
|
if (OptoReg::is_stack(reg)) return rc_stack;
|
|
VMReg r = OptoReg::as_VMReg(reg);
|
|
if (r->is_Register()) return rc_int;
|
|
assert(r->is_FloatRegister(), "must be");
|
|
return rc_float;
|
|
}
|
|
|
|
#ifndef PRODUCT
|
|
ATTRIBUTE_PRINTF(2, 3)
|
|
static void print_helper(outputStream* st, const char* format, ...) {
|
|
if (st->position() > 0) {
|
|
st->cr();
|
|
st->sp();
|
|
}
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
st->vprint(format, ap);
|
|
va_end(ap);
|
|
}
|
|
#endif // !PRODUCT
|
|
|
|
static void impl_helper(const MachNode* mach, CodeBuffer* cbuf, PhaseRegAlloc* ra, bool is_load, int offset, int reg, int opcode, const char *op_str, outputStream* st) {
|
|
if (cbuf) {
|
|
emit_form3_mem_reg(*cbuf, ra, mach, opcode, -1, R_SP_enc, offset, 0, Matcher::_regEncode[reg]);
|
|
}
|
|
#ifndef PRODUCT
|
|
else {
|
|
if (is_load) {
|
|
print_helper(st, "%s [R_SP + #%d],R_%s\t! spill", op_str, offset, OptoReg::regname(reg));
|
|
} else {
|
|
print_helper(st, "%s R_%s,[R_SP + #%d]\t! spill", op_str, OptoReg::regname(reg), offset);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void impl_mov_helper(CodeBuffer *cbuf, int src, int dst, int op1, int op2, const char *op_str, outputStream* st) {
|
|
if (cbuf) {
|
|
emit3(*cbuf, Assembler::arith_op, Matcher::_regEncode[dst], op1, 0, op2, Matcher::_regEncode[src]);
|
|
}
|
|
#ifndef PRODUCT
|
|
else {
|
|
print_helper(st, "%s R_%s,R_%s\t! spill", op_str, OptoReg::regname(src), OptoReg::regname(dst));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void mach_spill_copy_implementation_helper(const MachNode* mach,
|
|
CodeBuffer *cbuf,
|
|
PhaseRegAlloc *ra_,
|
|
outputStream* st) {
|
|
// Get registers to move
|
|
OptoReg::Name src_second = ra_->get_reg_second(mach->in(1));
|
|
OptoReg::Name src_first = ra_->get_reg_first(mach->in(1));
|
|
OptoReg::Name dst_second = ra_->get_reg_second(mach);
|
|
OptoReg::Name dst_first = ra_->get_reg_first(mach);
|
|
|
|
enum RC src_second_rc = rc_class(src_second);
|
|
enum RC src_first_rc = rc_class(src_first);
|
|
enum RC dst_second_rc = rc_class(dst_second);
|
|
enum RC dst_first_rc = rc_class(dst_first);
|
|
|
|
assert(OptoReg::is_valid(src_first) && OptoReg::is_valid(dst_first), "must move at least 1 register");
|
|
|
|
if (src_first == dst_first && src_second == dst_second) {
|
|
return; // Self copy, no move
|
|
}
|
|
|
|
// --------------------------------------
|
|
// Check for mem-mem move. Load into unused float registers and fall into
|
|
// the float-store case.
|
|
if (src_first_rc == rc_stack && dst_first_rc == rc_stack) {
|
|
int offset = ra_->reg2offset(src_first);
|
|
// Further check for aligned-adjacent pair, so we can use a double load
|
|
if ((src_first&1) == 0 && src_first+1 == src_second) {
|
|
src_second = OptoReg::Name(R_F31_num);
|
|
src_second_rc = rc_float;
|
|
impl_helper(mach, cbuf, ra_, true, offset, R_F30_num, Assembler::lddf_op3, "LDDF", st);
|
|
} else {
|
|
impl_helper(mach, cbuf, ra_, true, offset, R_F30_num, Assembler::ldf_op3, "LDF ", st);
|
|
}
|
|
src_first = OptoReg::Name(R_F30_num);
|
|
src_first_rc = rc_float;
|
|
}
|
|
|
|
if( src_second_rc == rc_stack && dst_second_rc == rc_stack ) {
|
|
int offset = ra_->reg2offset(src_second);
|
|
impl_helper(mach, cbuf, ra_, true, offset, R_F31_num, Assembler::ldf_op3, "LDF ", st);
|
|
src_second = OptoReg::Name(R_F31_num);
|
|
src_second_rc = rc_float;
|
|
}
|
|
|
|
// --------------------------------------
|
|
// Check for float->int copy; requires a trip through memory
|
|
if (src_first_rc == rc_float && dst_first_rc == rc_int && UseVIS < 3) {
|
|
int offset = frame::register_save_words*wordSize;
|
|
if (cbuf) {
|
|
emit3_simm13(*cbuf, Assembler::arith_op, R_SP_enc, Assembler::sub_op3, R_SP_enc, 16);
|
|
impl_helper(mach, cbuf, ra_, false, offset, src_first, Assembler::stf_op3, "STF ", st);
|
|
impl_helper(mach, cbuf, ra_, true, offset, dst_first, Assembler::lduw_op3, "LDUW", st);
|
|
emit3_simm13(*cbuf, Assembler::arith_op, R_SP_enc, Assembler::add_op3, R_SP_enc, 16);
|
|
}
|
|
#ifndef PRODUCT
|
|
else {
|
|
print_helper(st, "SUB R_SP,16,R_SP");
|
|
impl_helper(mach, cbuf, ra_, false, offset, src_first, Assembler::stf_op3, "STF ", st);
|
|
impl_helper(mach, cbuf, ra_, true, offset, dst_first, Assembler::lduw_op3, "LDUW", st);
|
|
print_helper(st, "ADD R_SP,16,R_SP");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Check for float->int copy on T4
|
|
if (src_first_rc == rc_float && dst_first_rc == rc_int && UseVIS >= 3) {
|
|
// Further check for aligned-adjacent pair, so we can use a double move
|
|
if ((src_first & 1) == 0 && src_first + 1 == src_second && (dst_first & 1) == 0 && dst_first + 1 == dst_second) {
|
|
impl_mov_helper(cbuf, src_first, dst_first, Assembler::mftoi_op3, Assembler::mdtox_opf, "MOVDTOX", st);
|
|
return;
|
|
}
|
|
impl_mov_helper(cbuf, src_first, dst_first, Assembler::mftoi_op3, Assembler::mstouw_opf, "MOVSTOUW", st);
|
|
}
|
|
// Check for int->float copy on T4
|
|
if (src_first_rc == rc_int && dst_first_rc == rc_float && UseVIS >= 3) {
|
|
// Further check for aligned-adjacent pair, so we can use a double move
|
|
if ((src_first & 1) == 0 && src_first + 1 == src_second && (dst_first & 1) == 0 && dst_first + 1 == dst_second) {
|
|
impl_mov_helper(cbuf, src_first, dst_first, Assembler::mftoi_op3, Assembler::mxtod_opf, "MOVXTOD", st);
|
|
return;
|
|
}
|
|
impl_mov_helper(cbuf, src_first, dst_first, Assembler::mftoi_op3, Assembler::mwtos_opf, "MOVWTOS", st);
|
|
}
|
|
|
|
// --------------------------------------
|
|
// In the 32-bit 1-reg-longs build ONLY, I see mis-aligned long destinations.
|
|
// In such cases, I have to do the big-endian swap. For aligned targets, the
|
|
// hardware does the flop for me. Doubles are always aligned, so no problem
|
|
// there. Misaligned sources only come from native-long-returns (handled
|
|
// special below).
|
|
|
|
// --------------------------------------
|
|
// Check for integer reg-reg copy
|
|
if (src_first_rc == rc_int && dst_first_rc == rc_int) {
|
|
// Else normal reg-reg copy
|
|
assert(src_second != dst_first, "smashed second before evacuating it");
|
|
impl_mov_helper(cbuf, src_first, dst_first, Assembler::or_op3, 0, "MOV ", st);
|
|
assert((src_first & 1) == 0 && (dst_first & 1) == 0, "never move second-halves of int registers");
|
|
// This moves an aligned adjacent pair.
|
|
// See if we are done.
|
|
if (src_first + 1 == src_second && dst_first + 1 == dst_second) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check for integer store
|
|
if (src_first_rc == rc_int && dst_first_rc == rc_stack) {
|
|
int offset = ra_->reg2offset(dst_first);
|
|
// Further check for aligned-adjacent pair, so we can use a double store
|
|
if ((src_first & 1) == 0 && src_first + 1 == src_second && (dst_first & 1) == 0 && dst_first + 1 == dst_second) {
|
|
impl_helper(mach, cbuf, ra_, false, offset, src_first, Assembler::stx_op3, "STX ", st);
|
|
return;
|
|
}
|
|
impl_helper(mach, cbuf, ra_, false, offset, src_first, Assembler::stw_op3, "STW ", st);
|
|
}
|
|
|
|
// Check for integer load
|
|
if (dst_first_rc == rc_int && src_first_rc == rc_stack) {
|
|
int offset = ra_->reg2offset(src_first);
|
|
// Further check for aligned-adjacent pair, so we can use a double load
|
|
if ((src_first & 1) == 0 && src_first + 1 == src_second && (dst_first & 1) == 0 && dst_first + 1 == dst_second) {
|
|
impl_helper(mach, cbuf, ra_, true, offset, dst_first, Assembler::ldx_op3, "LDX ", st);
|
|
return;
|
|
}
|
|
impl_helper(mach, cbuf, ra_, true, offset, dst_first, Assembler::lduw_op3, "LDUW", st);
|
|
}
|
|
|
|
// Check for float reg-reg copy
|
|
if (src_first_rc == rc_float && dst_first_rc == rc_float) {
|
|
// Further check for aligned-adjacent pair, so we can use a double move
|
|
if ((src_first & 1) == 0 && src_first + 1 == src_second && (dst_first & 1) == 0 && dst_first + 1 == dst_second) {
|
|
impl_mov_helper(cbuf, src_first, dst_first, Assembler::fpop1_op3, Assembler::fmovd_opf, "FMOVD", st);
|
|
return;
|
|
}
|
|
impl_mov_helper(cbuf, src_first, dst_first, Assembler::fpop1_op3, Assembler::fmovs_opf, "FMOVS", st);
|
|
}
|
|
|
|
// Check for float store
|
|
if (src_first_rc == rc_float && dst_first_rc == rc_stack) {
|
|
int offset = ra_->reg2offset(dst_first);
|
|
// Further check for aligned-adjacent pair, so we can use a double store
|
|
if ((src_first & 1) == 0 && src_first + 1 == src_second && (dst_first & 1) == 0 && dst_first + 1 == dst_second) {
|
|
impl_helper(mach, cbuf, ra_, false, offset, src_first, Assembler::stdf_op3, "STDF", st);
|
|
return;
|
|
}
|
|
impl_helper(mach, cbuf, ra_, false, offset, src_first, Assembler::stf_op3, "STF ", st);
|
|
}
|
|
|
|
// Check for float load
|
|
if (dst_first_rc == rc_float && src_first_rc == rc_stack) {
|
|
int offset = ra_->reg2offset(src_first);
|
|
// Further check for aligned-adjacent pair, so we can use a double load
|
|
if ((src_first & 1) == 0 && src_first + 1 == src_second && (dst_first & 1) == 0 && dst_first + 1 == dst_second) {
|
|
impl_helper(mach, cbuf, ra_, true, offset, dst_first, Assembler::lddf_op3, "LDDF", st);
|
|
return;
|
|
}
|
|
impl_helper(mach, cbuf, ra_, true, offset, dst_first, Assembler::ldf_op3, "LDF ", st);
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// Check for hi bits still needing moving. Only happens for misaligned
|
|
// arguments to native calls.
|
|
if (src_second == dst_second) {
|
|
return; // Self copy; no move
|
|
}
|
|
assert(src_second_rc != rc_bad && dst_second_rc != rc_bad, "src_second & dst_second cannot be Bad");
|
|
|
|
Unimplemented();
|
|
}
|
|
|
|
uint MachSpillCopyNode::implementation(CodeBuffer *cbuf,
|
|
PhaseRegAlloc *ra_,
|
|
bool do_size,
|
|
outputStream* st) const {
|
|
assert(!do_size, "not supported");
|
|
mach_spill_copy_implementation_helper(this, cbuf, ra_, st);
|
|
return 0;
|
|
}
|
|
|
|
#ifndef PRODUCT
|
|
void MachSpillCopyNode::format( PhaseRegAlloc *ra_, outputStream *st ) const {
|
|
implementation( NULL, ra_, false, st );
|
|
}
|
|
#endif
|
|
|
|
void MachSpillCopyNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
|
|
implementation( &cbuf, ra_, false, NULL );
|
|
}
|
|
|
|
uint MachSpillCopyNode::size(PhaseRegAlloc *ra_) const {
|
|
return MachNode::size(ra_);
|
|
}
|
|
|
|
//=============================================================================
|
|
#ifndef PRODUCT
|
|
void MachNopNode::format(PhaseRegAlloc *, outputStream *st) const {
|
|
st->print("NOP \t# %d bytes pad for loops and calls", 4 * _count);
|
|
}
|
|
#endif
|
|
|
|
void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *) const {
|
|
MacroAssembler _masm(&cbuf);
|
|
for (int i = 0; i < _count; i += 1) {
|
|
__ nop();
|
|
}
|
|
}
|
|
|
|
uint MachNopNode::size(PhaseRegAlloc *ra_) const {
|
|
return 4 * _count;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
#ifndef PRODUCT
|
|
void BoxLockNode::format( PhaseRegAlloc *ra_, outputStream *st ) const {
|
|
int offset = ra_->reg2offset(in_RegMask(0).find_first_elem());
|
|
int reg = ra_->get_reg_first(this);
|
|
st->print("LEA [R_SP+#%d+BIAS],%s",offset,Matcher::regName[reg]);
|
|
}
|
|
#endif
|
|
|
|
void BoxLockNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
|
|
MacroAssembler _masm(&cbuf);
|
|
int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()) + STACK_BIAS;
|
|
int reg = ra_->get_encode(this);
|
|
|
|
if (Assembler::is_simm13(offset)) {
|
|
__ add(SP, offset, reg_to_register_object(reg));
|
|
} else {
|
|
__ set(offset, O7);
|
|
__ add(SP, O7, reg_to_register_object(reg));
|
|
}
|
|
}
|
|
|
|
uint BoxLockNode::size(PhaseRegAlloc *ra_) const {
|
|
// BoxLockNode is not a MachNode, so we can't just call MachNode::size(ra_)
|
|
assert(ra_ == ra_->C->regalloc(), "sanity");
|
|
return ra_->C->scratch_emit_size(this);
|
|
}
|
|
|
|
//=============================================================================
|
|
#ifndef PRODUCT
|
|
void MachUEPNode::format( PhaseRegAlloc *ra_, outputStream *st ) const {
|
|
st->print_cr("\nUEP:");
|
|
if (UseCompressedClassPointers) {
|
|
assert(Universe::heap() != NULL, "java heap should be initialized");
|
|
st->print_cr("\tLDUW [R_O0 + oopDesc::klass_offset_in_bytes],R_G5\t! Inline cache check - compressed klass");
|
|
if (Universe::narrow_klass_base() != 0) {
|
|
st->print_cr("\tSET Universe::narrow_klass_base,R_G6_heap_base");
|
|
if (Universe::narrow_klass_shift() != 0) {
|
|
st->print_cr("\tSLL R_G5,Universe::narrow_klass_shift,R_G5");
|
|
}
|
|
st->print_cr("\tADD R_G5,R_G6_heap_base,R_G5");
|
|
st->print_cr("\tSET Universe::narrow_ptrs_base,R_G6_heap_base");
|
|
} else {
|
|
st->print_cr("\tSLL R_G5,Universe::narrow_klass_shift,R_G5");
|
|
}
|
|
} else {
|
|
st->print_cr("\tLDX [R_O0 + oopDesc::klass_offset_in_bytes],R_G5\t! Inline cache check");
|
|
}
|
|
st->print_cr("\tCMP R_G5,R_G3" );
|
|
st->print ("\tTne xcc,R_G0+ST_RESERVED_FOR_USER_0+2");
|
|
}
|
|
#endif
|
|
|
|
void MachUEPNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const {
|
|
MacroAssembler _masm(&cbuf);
|
|
Register G5_ic_reg = reg_to_register_object(Matcher::inline_cache_reg_encode());
|
|
Register temp_reg = G3;
|
|
assert( G5_ic_reg != temp_reg, "conflicting registers" );
|
|
|
|
// Load klass from receiver
|
|
__ load_klass(O0, temp_reg);
|
|
// Compare against expected klass
|
|
__ cmp(temp_reg, G5_ic_reg);
|
|
// Branch to miss code, checks xcc or icc depending
|
|
__ trap(Assembler::notEqual, Assembler::ptr_cc, G0, ST_RESERVED_FOR_USER_0+2);
|
|
}
|
|
|
|
uint MachUEPNode::size(PhaseRegAlloc *ra_) const {
|
|
return MachNode::size(ra_);
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
// Emit exception handler code.
|
|
int HandlerImpl::emit_exception_handler(CodeBuffer& cbuf) {
|
|
Register temp_reg = G3;
|
|
AddressLiteral exception_blob(OptoRuntime::exception_blob()->entry_point());
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
address base = __ start_a_stub(size_exception_handler());
|
|
if (base == NULL) {
|
|
ciEnv::current()->record_failure("CodeCache is full");
|
|
return 0; // CodeBuffer::expand failed
|
|
}
|
|
|
|
int offset = __ offset();
|
|
|
|
__ JUMP(exception_blob, temp_reg, 0); // sethi;jmp
|
|
__ delayed()->nop();
|
|
|
|
assert(__ offset() - offset <= (int) size_exception_handler(), "overflow");
|
|
|
|
__ end_a_stub();
|
|
|
|
return offset;
|
|
}
|
|
|
|
int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf) {
|
|
// Can't use any of the current frame's registers as we may have deopted
|
|
// at a poll and everything (including G3) can be live.
|
|
Register temp_reg = L0;
|
|
AddressLiteral deopt_blob(SharedRuntime::deopt_blob()->unpack());
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
address base = __ start_a_stub(size_deopt_handler());
|
|
if (base == NULL) {
|
|
ciEnv::current()->record_failure("CodeCache is full");
|
|
return 0; // CodeBuffer::expand failed
|
|
}
|
|
|
|
int offset = __ offset();
|
|
__ save_frame(0);
|
|
__ JUMP(deopt_blob, temp_reg, 0); // sethi;jmp
|
|
__ delayed()->restore();
|
|
|
|
assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow");
|
|
|
|
__ end_a_stub();
|
|
return offset;
|
|
|
|
}
|
|
|
|
// Given a register encoding, produce a Integer Register object
|
|
static Register reg_to_register_object(int register_encoding) {
|
|
assert(L5->encoding() == R_L5_enc && G1->encoding() == R_G1_enc, "right coding");
|
|
return as_Register(register_encoding);
|
|
}
|
|
|
|
// Given a register encoding, produce a single-precision Float Register object
|
|
static FloatRegister reg_to_SingleFloatRegister_object(int register_encoding) {
|
|
assert(F5->encoding(FloatRegisterImpl::S) == R_F5_enc && F12->encoding(FloatRegisterImpl::S) == R_F12_enc, "right coding");
|
|
return as_SingleFloatRegister(register_encoding);
|
|
}
|
|
|
|
// Given a register encoding, produce a double-precision Float Register object
|
|
static FloatRegister reg_to_DoubleFloatRegister_object(int register_encoding) {
|
|
assert(F4->encoding(FloatRegisterImpl::D) == R_F4_enc, "right coding");
|
|
assert(F32->encoding(FloatRegisterImpl::D) == R_D32_enc, "right coding");
|
|
return as_DoubleFloatRegister(register_encoding);
|
|
}
|
|
|
|
const bool Matcher::match_rule_supported(int opcode) {
|
|
if (!has_match_rule(opcode))
|
|
return false;
|
|
|
|
switch (opcode) {
|
|
case Op_CountLeadingZerosI:
|
|
case Op_CountLeadingZerosL:
|
|
case Op_CountTrailingZerosI:
|
|
case Op_CountTrailingZerosL:
|
|
case Op_PopCountI:
|
|
case Op_PopCountL:
|
|
if (!UsePopCountInstruction)
|
|
return false;
|
|
case Op_CompareAndSwapL:
|
|
case Op_CompareAndSwapP:
|
|
if (!VM_Version::supports_cx8())
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
return true; // Per default match rules are supported.
|
|
}
|
|
|
|
const bool Matcher::match_rule_supported_vector(int opcode, int vlen) {
|
|
|
|
// TODO
|
|
// identify extra cases that we might want to provide match rules for
|
|
// e.g. Op_ vector nodes and other intrinsics while guarding with vlen
|
|
bool ret_value = match_rule_supported(opcode);
|
|
// Add rules here.
|
|
|
|
return ret_value; // Per default match rules are supported.
|
|
}
|
|
|
|
const bool Matcher::has_predicated_vectors(void) {
|
|
return false;
|
|
}
|
|
|
|
const int Matcher::float_pressure(int default_pressure_threshold) {
|
|
return default_pressure_threshold;
|
|
}
|
|
|
|
int Matcher::regnum_to_fpu_offset(int regnum) {
|
|
return regnum - 32; // The FP registers are in the second chunk
|
|
}
|
|
|
|
#ifdef ASSERT
|
|
address last_rethrow = NULL; // debugging aid for Rethrow encoding
|
|
#endif
|
|
|
|
// Vector width in bytes
|
|
const int Matcher::vector_width_in_bytes(BasicType bt) {
|
|
assert(MaxVectorSize == 8, "");
|
|
return 8;
|
|
}
|
|
|
|
// Vector ideal reg
|
|
const uint Matcher::vector_ideal_reg(int size) {
|
|
assert(MaxVectorSize == 8, "");
|
|
return Op_RegD;
|
|
}
|
|
|
|
const uint Matcher::vector_shift_count_ideal_reg(int size) {
|
|
fatal("vector shift is not supported");
|
|
return Node::NotAMachineReg;
|
|
}
|
|
|
|
// Limits on vector size (number of elements) loaded into vector.
|
|
const int Matcher::max_vector_size(const BasicType bt) {
|
|
assert(is_java_primitive(bt), "only primitive type vectors");
|
|
return vector_width_in_bytes(bt)/type2aelembytes(bt);
|
|
}
|
|
|
|
const int Matcher::min_vector_size(const BasicType bt) {
|
|
return max_vector_size(bt); // Same as max.
|
|
}
|
|
|
|
// SPARC doesn't support misaligned vectors store/load.
|
|
const bool Matcher::misaligned_vectors_ok() {
|
|
return false;
|
|
}
|
|
|
|
// Current (2013) SPARC platforms need to read original key
|
|
// to construct decryption expanded key
|
|
const bool Matcher::pass_original_key_for_aes() {
|
|
return true;
|
|
}
|
|
|
|
// NOTE: All currently supported SPARC HW provides fast conversion.
|
|
const bool Matcher::convL2FSupported(void) { return true; }
|
|
|
|
// Is this branch offset short enough that a short branch can be used?
|
|
//
|
|
// NOTE: If the platform does not provide any short branch variants, then
|
|
// this method should return false for offset 0.
|
|
bool Matcher::is_short_branch_offset(int rule, int br_size, int offset) {
|
|
// The passed offset is relative to address of the branch.
|
|
// Don't need to adjust the offset.
|
|
return UseCBCond && Assembler::is_simm12(offset);
|
|
}
|
|
|
|
const bool Matcher::isSimpleConstant64(jlong value) {
|
|
// Will one (StoreL ConL) be cheaper than two (StoreI ConI)?.
|
|
// Depends on optimizations in MacroAssembler::setx.
|
|
int hi = (int)(value >> 32);
|
|
int lo = (int)(value & ~0);
|
|
return (hi == 0) || (hi == -1) || (lo == 0);
|
|
}
|
|
|
|
// No scaling for the parameter the ClearArray node.
|
|
const bool Matcher::init_array_count_is_in_bytes = true;
|
|
|
|
// No additional cost for CMOVL.
|
|
const int Matcher::long_cmove_cost() { return 0; }
|
|
|
|
// CMOVF/CMOVD are expensive on e.g., T4 and SPARC64.
|
|
const int Matcher::float_cmove_cost() {
|
|
return VM_Version::has_fast_cmove() ? 0 : ConditionalMoveLimit;
|
|
}
|
|
|
|
// Does the CPU require late expand (see block.cpp for description of late expand)?
|
|
const bool Matcher::require_postalloc_expand = false;
|
|
|
|
// Do we need to mask the count passed to shift instructions or does
|
|
// the cpu only look at the lower 5/6 bits anyway?
|
|
const bool Matcher::need_masked_shift_count = false;
|
|
|
|
bool Matcher::narrow_oop_use_complex_address() {
|
|
assert(UseCompressedOops, "only for compressed oops code");
|
|
return false;
|
|
}
|
|
|
|
bool Matcher::narrow_klass_use_complex_address() {
|
|
assert(UseCompressedClassPointers, "only for compressed klass code");
|
|
return false;
|
|
}
|
|
|
|
bool Matcher::const_oop_prefer_decode() {
|
|
// TODO: Check if loading ConP from TOC in heap-based mode is better:
|
|
// Prefer ConN+DecodeN over ConP in simple compressed oops mode.
|
|
// return Universe::narrow_oop_base() == NULL;
|
|
return true;
|
|
}
|
|
|
|
bool Matcher::const_klass_prefer_decode() {
|
|
// TODO: Check if loading ConP from TOC in heap-based mode is better:
|
|
// Prefer ConNKlass+DecodeNKlass over ConP in simple compressed klass mode.
|
|
// return Universe::narrow_klass_base() == NULL;
|
|
return true;
|
|
}
|
|
|
|
// Is it better to copy float constants, or load them directly from memory?
|
|
// Intel can load a float constant from a direct address, requiring no
|
|
// extra registers. Most RISCs will have to materialize an address into a
|
|
// register first, so they would do better to copy the constant from stack.
|
|
const bool Matcher::rematerialize_float_constants = false;
|
|
|
|
// If CPU can load and store mis-aligned doubles directly then no fixup is
|
|
// needed. Else we split the double into 2 integer pieces and move it
|
|
// piece-by-piece. Only happens when passing doubles into C code as the
|
|
// Java calling convention forces doubles to be aligned.
|
|
const bool Matcher::misaligned_doubles_ok = true;
|
|
|
|
// No-op on SPARC.
|
|
void Matcher::pd_implicit_null_fixup(MachNode *node, uint idx) {
|
|
}
|
|
|
|
// Advertise here if the CPU requires explicit rounding operations
|
|
// to implement the UseStrictFP mode.
|
|
const bool Matcher::strict_fp_requires_explicit_rounding = false;
|
|
|
|
// Are floats converted to double when stored to stack during deoptimization?
|
|
// Sparc does not handle callee-save floats.
|
|
bool Matcher::float_in_double() { return false; }
|
|
|
|
// Do ints take an entire long register or just half?
|
|
// Note that we if-def off of _LP64.
|
|
// The relevant question is how the int is callee-saved. In _LP64
|
|
// the whole long is written but de-opt'ing will have to extract
|
|
// the relevant 32 bits, in not-_LP64 only the low 32 bits is written.
|
|
const bool Matcher::int_in_long = true;
|
|
|
|
// Return whether or not this register is ever used as an argument. This
|
|
// function is used on startup to build the trampoline stubs in generateOptoStub.
|
|
// Registers not mentioned will be killed by the VM call in the trampoline, and
|
|
// arguments in those registers not be available to the callee.
|
|
bool Matcher::can_be_java_arg( int reg ) {
|
|
// Standard sparc 6 args in registers
|
|
if( reg == R_I0_num ||
|
|
reg == R_I1_num ||
|
|
reg == R_I2_num ||
|
|
reg == R_I3_num ||
|
|
reg == R_I4_num ||
|
|
reg == R_I5_num ) return true;
|
|
// 64-bit builds can pass 64-bit pointers and longs in
|
|
// the high I registers
|
|
if( reg == R_I0H_num ||
|
|
reg == R_I1H_num ||
|
|
reg == R_I2H_num ||
|
|
reg == R_I3H_num ||
|
|
reg == R_I4H_num ||
|
|
reg == R_I5H_num ) return true;
|
|
|
|
if ((UseCompressedOops) && (reg == R_G6_num || reg == R_G6H_num)) {
|
|
return true;
|
|
}
|
|
|
|
// A few float args in registers
|
|
if( reg >= R_F0_num && reg <= R_F7_num ) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Matcher::is_spillable_arg( int reg ) {
|
|
return can_be_java_arg(reg);
|
|
}
|
|
|
|
bool Matcher::use_asm_for_ldiv_by_con( jlong divisor ) {
|
|
// Use hardware SDIVX instruction when it is
|
|
// faster than a code which use multiply.
|
|
return VM_Version::has_fast_idiv();
|
|
}
|
|
|
|
// Register for DIVI projection of divmodI
|
|
RegMask Matcher::divI_proj_mask() {
|
|
ShouldNotReachHere();
|
|
return RegMask();
|
|
}
|
|
|
|
// Register for MODI projection of divmodI
|
|
RegMask Matcher::modI_proj_mask() {
|
|
ShouldNotReachHere();
|
|
return RegMask();
|
|
}
|
|
|
|
// Register for DIVL projection of divmodL
|
|
RegMask Matcher::divL_proj_mask() {
|
|
ShouldNotReachHere();
|
|
return RegMask();
|
|
}
|
|
|
|
// Register for MODL projection of divmodL
|
|
RegMask Matcher::modL_proj_mask() {
|
|
ShouldNotReachHere();
|
|
return RegMask();
|
|
}
|
|
|
|
const RegMask Matcher::method_handle_invoke_SP_save_mask() {
|
|
return L7_REGP_mask();
|
|
}
|
|
|
|
|
|
const bool Matcher::convi2l_type_required = true;
|
|
|
|
// Should the Matcher clone shifts on addressing modes, expecting them
|
|
// to be subsumed into complex addressing expressions or compute them
|
|
// into registers?
|
|
bool Matcher::clone_address_expressions(AddPNode* m, Matcher::MStack& mstack, VectorSet& address_visited) {
|
|
return clone_base_plus_offset_address(m, mstack, address_visited);
|
|
}
|
|
|
|
void Compile::reshape_address(AddPNode* addp) {
|
|
}
|
|
|
|
%}
|
|
|
|
|
|
// The intptr_t operand types, defined by textual substitution.
|
|
// (Cf. opto/type.hpp. This lets us avoid many, many other ifdefs.)
|
|
#define immX immL
|
|
#define immX13 immL13
|
|
#define immX13m7 immL13m7
|
|
#define iRegX iRegL
|
|
#define g1RegX g1RegL
|
|
|
|
//----------ENCODING BLOCK-----------------------------------------------------
|
|
// This block specifies the encoding classes used by the compiler to output
|
|
// byte streams. Encoding classes are parameterized macros used by
|
|
// Machine Instruction Nodes in order to generate the bit encoding of the
|
|
// instruction. Operands specify their base encoding interface with the
|
|
// interface keyword. There are currently supported four interfaces,
|
|
// REG_INTER, CONST_INTER, MEMORY_INTER, & COND_INTER. REG_INTER causes an
|
|
// operand to generate a function which returns its register number when
|
|
// queried. CONST_INTER causes an operand to generate a function which
|
|
// returns the value of the constant when queried. MEMORY_INTER causes an
|
|
// operand to generate four functions which return the Base Register, the
|
|
// Index Register, the Scale Value, and the Offset Value of the operand when
|
|
// queried. COND_INTER causes an operand to generate six functions which
|
|
// return the encoding code (ie - encoding bits for the instruction)
|
|
// associated with each basic boolean condition for a conditional instruction.
|
|
//
|
|
// Instructions specify two basic values for encoding. Again, a function
|
|
// is available to check if the constant displacement is an oop. They use the
|
|
// ins_encode keyword to specify their encoding classes (which must be
|
|
// a sequence of enc_class names, and their parameters, specified in
|
|
// the encoding block), and they use the
|
|
// opcode keyword to specify, in order, their primary, secondary, and
|
|
// tertiary opcode. Only the opcode sections which a particular instruction
|
|
// needs for encoding need to be specified.
|
|
encode %{
|
|
enc_class enc_untested %{
|
|
#ifdef ASSERT
|
|
MacroAssembler _masm(&cbuf);
|
|
__ untested("encoding");
|
|
#endif
|
|
%}
|
|
|
|
enc_class form3_mem_reg( memory mem, iRegI dst ) %{
|
|
emit_form3_mem_reg(cbuf, ra_, this, $primary, $tertiary,
|
|
$mem$$base, $mem$$disp, $mem$$index, $dst$$reg);
|
|
%}
|
|
|
|
enc_class simple_form3_mem_reg( memory mem, iRegI dst ) %{
|
|
emit_form3_mem_reg(cbuf, ra_, this, $primary, -1,
|
|
$mem$$base, $mem$$disp, $mem$$index, $dst$$reg);
|
|
%}
|
|
|
|
enc_class form3_mem_prefetch_read( memory mem ) %{
|
|
emit_form3_mem_reg(cbuf, ra_, this, $primary, -1,
|
|
$mem$$base, $mem$$disp, $mem$$index, 0/*prefetch function many-reads*/);
|
|
%}
|
|
|
|
enc_class form3_mem_prefetch_write( memory mem ) %{
|
|
emit_form3_mem_reg(cbuf, ra_, this, $primary, -1,
|
|
$mem$$base, $mem$$disp, $mem$$index, 2/*prefetch function many-writes*/);
|
|
%}
|
|
|
|
enc_class form3_mem_reg_long_unaligned_marshal( memory mem, iRegL reg ) %{
|
|
assert(Assembler::is_simm13($mem$$disp ), "need disp and disp+4");
|
|
assert(Assembler::is_simm13($mem$$disp+4), "need disp and disp+4");
|
|
guarantee($mem$$index == R_G0_enc, "double index?");
|
|
emit_form3_mem_reg(cbuf, ra_, this, $primary, -1, $mem$$base, $mem$$disp+4, R_G0_enc, R_O7_enc );
|
|
emit_form3_mem_reg(cbuf, ra_, this, $primary, -1, $mem$$base, $mem$$disp, R_G0_enc, $reg$$reg );
|
|
emit3_simm13( cbuf, Assembler::arith_op, $reg$$reg, Assembler::sllx_op3, $reg$$reg, 0x1020 );
|
|
emit3( cbuf, Assembler::arith_op, $reg$$reg, Assembler::or_op3, $reg$$reg, 0, R_O7_enc );
|
|
%}
|
|
|
|
enc_class form3_mem_reg_double_unaligned( memory mem, RegD_low reg ) %{
|
|
assert(Assembler::is_simm13($mem$$disp ), "need disp and disp+4");
|
|
assert(Assembler::is_simm13($mem$$disp+4), "need disp and disp+4");
|
|
guarantee($mem$$index == R_G0_enc, "double index?");
|
|
// Load long with 2 instructions
|
|
emit_form3_mem_reg(cbuf, ra_, this, $primary, -1, $mem$$base, $mem$$disp, R_G0_enc, $reg$$reg+0 );
|
|
emit_form3_mem_reg(cbuf, ra_, this, $primary, -1, $mem$$base, $mem$$disp+4, R_G0_enc, $reg$$reg+1 );
|
|
%}
|
|
|
|
//%%% form3_mem_plus_4_reg is a hack--get rid of it
|
|
enc_class form3_mem_plus_4_reg( memory mem, iRegI dst ) %{
|
|
guarantee($mem$$disp, "cannot offset a reg-reg operand by 4");
|
|
emit_form3_mem_reg(cbuf, ra_, this, $primary, -1, $mem$$base, $mem$$disp + 4, $mem$$index, $dst$$reg);
|
|
%}
|
|
|
|
enc_class form3_g0_rs2_rd_move( iRegI rs2, iRegI rd ) %{
|
|
// Encode a reg-reg copy. If it is useless, then empty encoding.
|
|
if( $rs2$$reg != $rd$$reg )
|
|
emit3( cbuf, Assembler::arith_op, $rd$$reg, Assembler::or_op3, 0, 0, $rs2$$reg );
|
|
%}
|
|
|
|
// Target lo half of long
|
|
enc_class form3_g0_rs2_rd_move_lo( iRegI rs2, iRegL rd ) %{
|
|
// Encode a reg-reg copy. If it is useless, then empty encoding.
|
|
if( $rs2$$reg != LONG_LO_REG($rd$$reg) )
|
|
emit3( cbuf, Assembler::arith_op, LONG_LO_REG($rd$$reg), Assembler::or_op3, 0, 0, $rs2$$reg );
|
|
%}
|
|
|
|
// Source lo half of long
|
|
enc_class form3_g0_rs2_rd_move_lo2( iRegL rs2, iRegI rd ) %{
|
|
// Encode a reg-reg copy. If it is useless, then empty encoding.
|
|
if( LONG_LO_REG($rs2$$reg) != $rd$$reg )
|
|
emit3( cbuf, Assembler::arith_op, $rd$$reg, Assembler::or_op3, 0, 0, LONG_LO_REG($rs2$$reg) );
|
|
%}
|
|
|
|
// Target hi half of long
|
|
enc_class form3_rs1_rd_copysign_hi( iRegI rs1, iRegL rd ) %{
|
|
emit3_simm13( cbuf, Assembler::arith_op, $rd$$reg, Assembler::sra_op3, $rs1$$reg, 31 );
|
|
%}
|
|
|
|
// Source lo half of long, and leave it sign extended.
|
|
enc_class form3_rs1_rd_signextend_lo1( iRegL rs1, iRegI rd ) %{
|
|
// Sign extend low half
|
|
emit3( cbuf, Assembler::arith_op, $rd$$reg, Assembler::sra_op3, $rs1$$reg, 0, 0 );
|
|
%}
|
|
|
|
// Source hi half of long, and leave it sign extended.
|
|
enc_class form3_rs1_rd_copy_hi1( iRegL rs1, iRegI rd ) %{
|
|
// Shift high half to low half
|
|
emit3_simm13( cbuf, Assembler::arith_op, $rd$$reg, Assembler::srlx_op3, $rs1$$reg, 32 );
|
|
%}
|
|
|
|
// Source hi half of long
|
|
enc_class form3_g0_rs2_rd_move_hi2( iRegL rs2, iRegI rd ) %{
|
|
// Encode a reg-reg copy. If it is useless, then empty encoding.
|
|
if( LONG_HI_REG($rs2$$reg) != $rd$$reg )
|
|
emit3( cbuf, Assembler::arith_op, $rd$$reg, Assembler::or_op3, 0, 0, LONG_HI_REG($rs2$$reg) );
|
|
%}
|
|
|
|
enc_class form3_rs1_rs2_rd( iRegI rs1, iRegI rs2, iRegI rd ) %{
|
|
emit3( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, 0, $rs2$$reg );
|
|
%}
|
|
|
|
enc_class enc_to_bool( iRegI src, iRegI dst ) %{
|
|
emit3 ( cbuf, Assembler::arith_op, 0, Assembler::subcc_op3, 0, 0, $src$$reg );
|
|
emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::addc_op3 , 0, 0 );
|
|
%}
|
|
|
|
enc_class enc_ltmask( iRegI p, iRegI q, iRegI dst ) %{
|
|
emit3 ( cbuf, Assembler::arith_op, 0, Assembler::subcc_op3, $p$$reg, 0, $q$$reg );
|
|
// clear if nothing else is happening
|
|
emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3, 0, 0 );
|
|
// blt,a,pn done
|
|
emit2_19 ( cbuf, Assembler::branch_op, 1/*annul*/, Assembler::less, Assembler::bp_op2, Assembler::icc, 0/*predict not taken*/, 2 );
|
|
// mov dst,-1 in delay slot
|
|
emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3, 0, -1 );
|
|
%}
|
|
|
|
enc_class form3_rs1_imm5_rd( iRegI rs1, immU5 imm5, iRegI rd ) %{
|
|
emit3_simm13( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, $imm5$$constant & 0x1F );
|
|
%}
|
|
|
|
enc_class form3_sd_rs1_imm6_rd( iRegL rs1, immU6 imm6, iRegL rd ) %{
|
|
emit3_simm13( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, ($imm6$$constant & 0x3F) | 0x1000 );
|
|
%}
|
|
|
|
enc_class form3_sd_rs1_rs2_rd( iRegL rs1, iRegI rs2, iRegL rd ) %{
|
|
emit3( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, 0x80, $rs2$$reg );
|
|
%}
|
|
|
|
enc_class form3_rs1_simm13_rd( iRegI rs1, immI13 simm13, iRegI rd ) %{
|
|
emit3_simm13( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, $simm13$$constant );
|
|
%}
|
|
|
|
enc_class move_return_pc_to_o1() %{
|
|
emit3_simm13( cbuf, Assembler::arith_op, R_O1_enc, Assembler::add_op3, R_O7_enc, frame::pc_return_offset );
|
|
%}
|
|
|
|
/* %%% merge with enc_to_bool */
|
|
enc_class enc_convP2B( iRegI dst, iRegP src ) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
Register src_reg = reg_to_register_object($src$$reg);
|
|
Register dst_reg = reg_to_register_object($dst$$reg);
|
|
__ movr(Assembler::rc_nz, src_reg, 1, dst_reg);
|
|
%}
|
|
|
|
enc_class enc_cadd_cmpLTMask( iRegI p, iRegI q, iRegI y, iRegI tmp ) %{
|
|
// (Set p (AddI (AndI (CmpLTMask p q) y) (SubI p q)))
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
Register p_reg = reg_to_register_object($p$$reg);
|
|
Register q_reg = reg_to_register_object($q$$reg);
|
|
Register y_reg = reg_to_register_object($y$$reg);
|
|
Register tmp_reg = reg_to_register_object($tmp$$reg);
|
|
|
|
__ subcc( p_reg, q_reg, p_reg );
|
|
__ add ( p_reg, y_reg, tmp_reg );
|
|
__ movcc( Assembler::less, false, Assembler::icc, tmp_reg, p_reg );
|
|
%}
|
|
|
|
enc_class form_d2i_helper(regD src, regF dst) %{
|
|
// fcmp %fcc0,$src,$src
|
|
emit3( cbuf, Assembler::arith_op , Assembler::fcc0, Assembler::fpop2_op3, $src$$reg, Assembler::fcmpd_opf, $src$$reg );
|
|
// branch %fcc0 not-nan, predict taken
|
|
emit2_19( cbuf, Assembler::branch_op, 0/*annul*/, Assembler::f_ordered, Assembler::fbp_op2, Assembler::fcc0, 1/*predict taken*/, 4 );
|
|
// fdtoi $src,$dst
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fdtoi_opf, $src$$reg );
|
|
// fitos $dst,$dst (if nan)
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fitos_opf, $dst$$reg );
|
|
// clear $dst (if nan)
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, $dst$$reg, Assembler::fsubs_opf, $dst$$reg );
|
|
// carry on here...
|
|
%}
|
|
|
|
enc_class form_d2l_helper(regD src, regD dst) %{
|
|
// fcmp %fcc0,$src,$src check for NAN
|
|
emit3( cbuf, Assembler::arith_op , Assembler::fcc0, Assembler::fpop2_op3, $src$$reg, Assembler::fcmpd_opf, $src$$reg );
|
|
// branch %fcc0 not-nan, predict taken
|
|
emit2_19( cbuf, Assembler::branch_op, 0/*annul*/, Assembler::f_ordered, Assembler::fbp_op2, Assembler::fcc0, 1/*predict taken*/, 4 );
|
|
// fdtox $src,$dst convert in delay slot
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fdtox_opf, $src$$reg );
|
|
// fxtod $dst,$dst (if nan)
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fxtod_opf, $dst$$reg );
|
|
// clear $dst (if nan)
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, $dst$$reg, Assembler::fsubd_opf, $dst$$reg );
|
|
// carry on here...
|
|
%}
|
|
|
|
enc_class form_f2i_helper(regF src, regF dst) %{
|
|
// fcmps %fcc0,$src,$src
|
|
emit3( cbuf, Assembler::arith_op , Assembler::fcc0, Assembler::fpop2_op3, $src$$reg, Assembler::fcmps_opf, $src$$reg );
|
|
// branch %fcc0 not-nan, predict taken
|
|
emit2_19( cbuf, Assembler::branch_op, 0/*annul*/, Assembler::f_ordered, Assembler::fbp_op2, Assembler::fcc0, 1/*predict taken*/, 4 );
|
|
// fstoi $src,$dst
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fstoi_opf, $src$$reg );
|
|
// fitos $dst,$dst (if nan)
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fitos_opf, $dst$$reg );
|
|
// clear $dst (if nan)
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, $dst$$reg, Assembler::fsubs_opf, $dst$$reg );
|
|
// carry on here...
|
|
%}
|
|
|
|
enc_class form_f2l_helper(regF src, regD dst) %{
|
|
// fcmps %fcc0,$src,$src
|
|
emit3( cbuf, Assembler::arith_op , Assembler::fcc0, Assembler::fpop2_op3, $src$$reg, Assembler::fcmps_opf, $src$$reg );
|
|
// branch %fcc0 not-nan, predict taken
|
|
emit2_19( cbuf, Assembler::branch_op, 0/*annul*/, Assembler::f_ordered, Assembler::fbp_op2, Assembler::fcc0, 1/*predict taken*/, 4 );
|
|
// fstox $src,$dst
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fstox_opf, $src$$reg );
|
|
// fxtod $dst,$dst (if nan)
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, 0, Assembler::fxtod_opf, $dst$$reg );
|
|
// clear $dst (if nan)
|
|
emit3( cbuf, Assembler::arith_op , $dst$$reg, Assembler::fpop1_op3, $dst$$reg, Assembler::fsubd_opf, $dst$$reg );
|
|
// carry on here...
|
|
%}
|
|
|
|
enc_class form3_opf_rs2F_rdF(regF rs2, regF rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %}
|
|
enc_class form3_opf_rs2F_rdD(regF rs2, regD rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %}
|
|
enc_class form3_opf_rs2D_rdF(regD rs2, regF rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %}
|
|
enc_class form3_opf_rs2D_rdD(regD rs2, regD rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %}
|
|
|
|
enc_class form3_opf_rs2D_lo_rdF(regD rs2, regF rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg+1); %}
|
|
|
|
enc_class form3_opf_rs2D_hi_rdD_hi(regD rs2, regD rd) %{ emit3(cbuf,$secondary,$rd$$reg,$primary,0,$tertiary,$rs2$$reg); %}
|
|
enc_class form3_opf_rs2D_lo_rdD_lo(regD rs2, regD rd) %{ emit3(cbuf,$secondary,$rd$$reg+1,$primary,0,$tertiary,$rs2$$reg+1); %}
|
|
|
|
enc_class form3_opf_rs1F_rs2F_rdF( regF rs1, regF rs2, regF rd ) %{
|
|
emit3( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, $tertiary, $rs2$$reg );
|
|
%}
|
|
|
|
enc_class form3_opf_rs1D_rs2D_rdD( regD rs1, regD rs2, regD rd ) %{
|
|
emit3( cbuf, $secondary, $rd$$reg, $primary, $rs1$$reg, $tertiary, $rs2$$reg );
|
|
%}
|
|
|
|
enc_class form3_opf_rs1F_rs2F_fcc( regF rs1, regF rs2, flagsRegF fcc ) %{
|
|
emit3( cbuf, $secondary, $fcc$$reg, $primary, $rs1$$reg, $tertiary, $rs2$$reg );
|
|
%}
|
|
|
|
enc_class form3_opf_rs1D_rs2D_fcc( regD rs1, regD rs2, flagsRegF fcc ) %{
|
|
emit3( cbuf, $secondary, $fcc$$reg, $primary, $rs1$$reg, $tertiary, $rs2$$reg );
|
|
%}
|
|
|
|
enc_class form3_convI2F(regF rs2, regF rd) %{
|
|
emit3(cbuf,Assembler::arith_op,$rd$$reg,Assembler::fpop1_op3,0,$secondary,$rs2$$reg);
|
|
%}
|
|
|
|
// Encloding class for traceable jumps
|
|
enc_class form_jmpl(g3RegP dest) %{
|
|
emit_jmpl(cbuf, $dest$$reg);
|
|
%}
|
|
|
|
enc_class form_jmpl_set_exception_pc(g1RegP dest) %{
|
|
emit_jmpl_set_exception_pc(cbuf, $dest$$reg);
|
|
%}
|
|
|
|
enc_class form2_nop() %{
|
|
emit_nop(cbuf);
|
|
%}
|
|
|
|
enc_class form2_illtrap() %{
|
|
emit_illtrap(cbuf);
|
|
%}
|
|
|
|
|
|
// Compare longs and convert into -1, 0, 1.
|
|
enc_class cmpl_flag( iRegL src1, iRegL src2, iRegI dst ) %{
|
|
// CMP $src1,$src2
|
|
emit3( cbuf, Assembler::arith_op, 0, Assembler::subcc_op3, $src1$$reg, 0, $src2$$reg );
|
|
// blt,a,pn done
|
|
emit2_19( cbuf, Assembler::branch_op, 1/*annul*/, Assembler::less , Assembler::bp_op2, Assembler::xcc, 0/*predict not taken*/, 5 );
|
|
// mov dst,-1 in delay slot
|
|
emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3, 0, -1 );
|
|
// bgt,a,pn done
|
|
emit2_19( cbuf, Assembler::branch_op, 1/*annul*/, Assembler::greater, Assembler::bp_op2, Assembler::xcc, 0/*predict not taken*/, 3 );
|
|
// mov dst,1 in delay slot
|
|
emit3_simm13( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3, 0, 1 );
|
|
// CLR $dst
|
|
emit3( cbuf, Assembler::arith_op, $dst$$reg, Assembler::or_op3 , 0, 0, 0 );
|
|
%}
|
|
|
|
enc_class enc_PartialSubtypeCheck() %{
|
|
MacroAssembler _masm(&cbuf);
|
|
__ call(StubRoutines::Sparc::partial_subtype_check(), relocInfo::runtime_call_type);
|
|
__ delayed()->nop();
|
|
%}
|
|
|
|
enc_class enc_bp( label labl, cmpOp cmp, flagsReg cc ) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
|
|
__ bp( (Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
|
|
enc_class enc_bpr( label labl, cmpOp_reg cmp, iRegI op1 ) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
|
|
__ bpr( (Assembler::RCondition)($cmp$$cmpcode), false, predict_taken, as_Register($op1$$reg), *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
|
|
enc_class enc_cmov_reg( cmpOp cmp, iRegI dst, iRegI src, immI pcc) %{
|
|
int op = (Assembler::arith_op << 30) |
|
|
($dst$$reg << 25) |
|
|
(Assembler::movcc_op3 << 19) |
|
|
(1 << 18) | // cc2 bit for 'icc'
|
|
($cmp$$cmpcode << 14) |
|
|
(0 << 13) | // select register move
|
|
($pcc$$constant << 11) | // cc1, cc0 bits for 'icc' or 'xcc'
|
|
($src$$reg << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
%}
|
|
|
|
enc_class enc_cmov_imm( cmpOp cmp, iRegI dst, immI11 src, immI pcc ) %{
|
|
int simm11 = $src$$constant & ((1<<11)-1); // Mask to 11 bits
|
|
int op = (Assembler::arith_op << 30) |
|
|
($dst$$reg << 25) |
|
|
(Assembler::movcc_op3 << 19) |
|
|
(1 << 18) | // cc2 bit for 'icc'
|
|
($cmp$$cmpcode << 14) |
|
|
(1 << 13) | // select immediate move
|
|
($pcc$$constant << 11) | // cc1, cc0 bits for 'icc'
|
|
(simm11 << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
%}
|
|
|
|
enc_class enc_cmov_reg_f( cmpOpF cmp, iRegI dst, iRegI src, flagsRegF fcc ) %{
|
|
int op = (Assembler::arith_op << 30) |
|
|
($dst$$reg << 25) |
|
|
(Assembler::movcc_op3 << 19) |
|
|
(0 << 18) | // cc2 bit for 'fccX'
|
|
($cmp$$cmpcode << 14) |
|
|
(0 << 13) | // select register move
|
|
($fcc$$reg << 11) | // cc1, cc0 bits for fcc0-fcc3
|
|
($src$$reg << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
%}
|
|
|
|
enc_class enc_cmov_imm_f( cmpOp cmp, iRegI dst, immI11 src, flagsRegF fcc ) %{
|
|
int simm11 = $src$$constant & ((1<<11)-1); // Mask to 11 bits
|
|
int op = (Assembler::arith_op << 30) |
|
|
($dst$$reg << 25) |
|
|
(Assembler::movcc_op3 << 19) |
|
|
(0 << 18) | // cc2 bit for 'fccX'
|
|
($cmp$$cmpcode << 14) |
|
|
(1 << 13) | // select immediate move
|
|
($fcc$$reg << 11) | // cc1, cc0 bits for fcc0-fcc3
|
|
(simm11 << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
%}
|
|
|
|
enc_class enc_cmovf_reg( cmpOp cmp, regD dst, regD src, immI pcc ) %{
|
|
int op = (Assembler::arith_op << 30) |
|
|
($dst$$reg << 25) |
|
|
(Assembler::fpop2_op3 << 19) |
|
|
(0 << 18) |
|
|
($cmp$$cmpcode << 14) |
|
|
(1 << 13) | // select register move
|
|
($pcc$$constant << 11) | // cc1-cc0 bits for 'icc' or 'xcc'
|
|
($primary << 5) | // select single, double or quad
|
|
($src$$reg << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
%}
|
|
|
|
enc_class enc_cmovff_reg( cmpOpF cmp, flagsRegF fcc, regD dst, regD src ) %{
|
|
int op = (Assembler::arith_op << 30) |
|
|
($dst$$reg << 25) |
|
|
(Assembler::fpop2_op3 << 19) |
|
|
(0 << 18) |
|
|
($cmp$$cmpcode << 14) |
|
|
($fcc$$reg << 11) | // cc2-cc0 bits for 'fccX'
|
|
($primary << 5) | // select single, double or quad
|
|
($src$$reg << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
%}
|
|
|
|
// Used by the MIN/MAX encodings. Same as a CMOV, but
|
|
// the condition comes from opcode-field instead of an argument.
|
|
enc_class enc_cmov_reg_minmax( iRegI dst, iRegI src ) %{
|
|
int op = (Assembler::arith_op << 30) |
|
|
($dst$$reg << 25) |
|
|
(Assembler::movcc_op3 << 19) |
|
|
(1 << 18) | // cc2 bit for 'icc'
|
|
($primary << 14) |
|
|
(0 << 13) | // select register move
|
|
(0 << 11) | // cc1, cc0 bits for 'icc'
|
|
($src$$reg << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
%}
|
|
|
|
enc_class enc_cmov_reg_minmax_long( iRegL dst, iRegL src ) %{
|
|
int op = (Assembler::arith_op << 30) |
|
|
($dst$$reg << 25) |
|
|
(Assembler::movcc_op3 << 19) |
|
|
(6 << 16) | // cc2 bit for 'xcc'
|
|
($primary << 14) |
|
|
(0 << 13) | // select register move
|
|
(0 << 11) | // cc1, cc0 bits for 'icc'
|
|
($src$$reg << 0);
|
|
cbuf.insts()->emit_int32(op);
|
|
%}
|
|
|
|
enc_class Set13( immI13 src, iRegI rd ) %{
|
|
emit3_simm13( cbuf, Assembler::arith_op, $rd$$reg, Assembler::or_op3, 0, $src$$constant );
|
|
%}
|
|
|
|
enc_class SetHi22( immI src, iRegI rd ) %{
|
|
emit2_22( cbuf, Assembler::branch_op, $rd$$reg, Assembler::sethi_op2, $src$$constant );
|
|
%}
|
|
|
|
enc_class Set32( immI src, iRegI rd ) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
__ set($src$$constant, reg_to_register_object($rd$$reg));
|
|
%}
|
|
|
|
enc_class call_epilog %{
|
|
if( VerifyStackAtCalls ) {
|
|
MacroAssembler _masm(&cbuf);
|
|
int framesize = ra_->C->frame_size_in_bytes();
|
|
Register temp_reg = G3;
|
|
__ add(SP, framesize, temp_reg);
|
|
__ cmp(temp_reg, FP);
|
|
__ breakpoint_trap(Assembler::notEqual, Assembler::ptr_cc);
|
|
}
|
|
%}
|
|
|
|
// Long values come back from native calls in O0:O1 in the 32-bit VM, copy the value
|
|
// to G1 so the register allocator will not have to deal with the misaligned register
|
|
// pair.
|
|
enc_class adjust_long_from_native_call %{
|
|
%}
|
|
|
|
enc_class Java_To_Runtime (method meth) %{ // CALL Java_To_Runtime
|
|
// CALL directly to the runtime
|
|
// The user of this is responsible for ensuring that R_L7 is empty (killed).
|
|
emit_call_reloc(cbuf, $meth$$method, runtime_call_Relocation::spec(), /*preserve_g2=*/true);
|
|
%}
|
|
|
|
enc_class preserve_SP %{
|
|
MacroAssembler _masm(&cbuf);
|
|
__ mov(SP, L7_mh_SP_save);
|
|
%}
|
|
|
|
enc_class restore_SP %{
|
|
MacroAssembler _masm(&cbuf);
|
|
__ mov(L7_mh_SP_save, SP);
|
|
%}
|
|
|
|
enc_class Java_Static_Call (method meth) %{ // JAVA STATIC CALL
|
|
// CALL to fixup routine. Fixup routine uses ScopeDesc info to determine
|
|
// who we intended to call.
|
|
if (!_method) {
|
|
emit_call_reloc(cbuf, $meth$$method, runtime_call_Relocation::spec());
|
|
} else {
|
|
int method_index = resolved_method_index(cbuf);
|
|
RelocationHolder rspec = _optimized_virtual ? opt_virtual_call_Relocation::spec(method_index)
|
|
: static_call_Relocation::spec(method_index);
|
|
emit_call_reloc(cbuf, $meth$$method, rspec);
|
|
|
|
// Emit stub for static call.
|
|
address stub = CompiledStaticCall::emit_to_interp_stub(cbuf);
|
|
if (stub == NULL) {
|
|
ciEnv::current()->record_failure("CodeCache is full");
|
|
return;
|
|
}
|
|
}
|
|
%}
|
|
|
|
enc_class Java_Dynamic_Call (method meth) %{ // JAVA DYNAMIC CALL
|
|
MacroAssembler _masm(&cbuf);
|
|
__ set_inst_mark();
|
|
int vtable_index = this->_vtable_index;
|
|
// MachCallDynamicJavaNode::ret_addr_offset uses this same test
|
|
if (vtable_index < 0) {
|
|
// must be invalid_vtable_index, not nonvirtual_vtable_index
|
|
assert(vtable_index == Method::invalid_vtable_index, "correct sentinel value");
|
|
Register G5_ic_reg = reg_to_register_object(Matcher::inline_cache_reg_encode());
|
|
assert(G5_ic_reg == G5_inline_cache_reg, "G5_inline_cache_reg used in assemble_ic_buffer_code()");
|
|
assert(G5_ic_reg == G5_megamorphic_method, "G5_megamorphic_method used in megamorphic call stub");
|
|
__ ic_call((address)$meth$$method, /*emit_delay=*/true, resolved_method_index(cbuf));
|
|
} else {
|
|
assert(!UseInlineCaches, "expect vtable calls only if not using ICs");
|
|
// Just go thru the vtable
|
|
// get receiver klass (receiver already checked for non-null)
|
|
// If we end up going thru a c2i adapter interpreter expects method in G5
|
|
int off = __ offset();
|
|
__ load_klass(O0, G3_scratch);
|
|
int klass_load_size;
|
|
if (UseCompressedClassPointers) {
|
|
assert(Universe::heap() != NULL, "java heap should be initialized");
|
|
klass_load_size = MacroAssembler::instr_size_for_decode_klass_not_null() + 1*BytesPerInstWord;
|
|
} else {
|
|
klass_load_size = 1*BytesPerInstWord;
|
|
}
|
|
int entry_offset = in_bytes(Klass::vtable_start_offset()) + vtable_index*vtableEntry::size_in_bytes();
|
|
int v_off = entry_offset + vtableEntry::method_offset_in_bytes();
|
|
if (Assembler::is_simm13(v_off)) {
|
|
__ ld_ptr(G3, v_off, G5_method);
|
|
} else {
|
|
// Generate 2 instructions
|
|
__ Assembler::sethi(v_off & ~0x3ff, G5_method);
|
|
__ or3(G5_method, v_off & 0x3ff, G5_method);
|
|
// ld_ptr, set_hi, set
|
|
assert(__ offset() - off == klass_load_size + 2*BytesPerInstWord,
|
|
"Unexpected instruction size(s)");
|
|
__ ld_ptr(G3, G5_method, G5_method);
|
|
}
|
|
// NOTE: for vtable dispatches, the vtable entry will never be null.
|
|
// However it may very well end up in handle_wrong_method if the
|
|
// method is abstract for the particular class.
|
|
__ ld_ptr(G5_method, in_bytes(Method::from_compiled_offset()), G3_scratch);
|
|
// jump to target (either compiled code or c2iadapter)
|
|
__ jmpl(G3_scratch, G0, O7);
|
|
__ delayed()->nop();
|
|
}
|
|
%}
|
|
|
|
enc_class Java_Compiled_Call (method meth) %{ // JAVA COMPILED CALL
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
Register G5_ic_reg = reg_to_register_object(Matcher::inline_cache_reg_encode());
|
|
Register temp_reg = G3; // caller must kill G3! We cannot reuse G5_ic_reg here because
|
|
// we might be calling a C2I adapter which needs it.
|
|
|
|
assert(temp_reg != G5_ic_reg, "conflicting registers");
|
|
// Load nmethod
|
|
__ ld_ptr(G5_ic_reg, in_bytes(Method::from_compiled_offset()), temp_reg);
|
|
|
|
// CALL to compiled java, indirect the contents of G3
|
|
__ set_inst_mark();
|
|
__ callr(temp_reg, G0);
|
|
__ delayed()->nop();
|
|
%}
|
|
|
|
enc_class idiv_reg(iRegIsafe src1, iRegIsafe src2, iRegIsafe dst) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
Register Rdividend = reg_to_register_object($src1$$reg);
|
|
Register Rdivisor = reg_to_register_object($src2$$reg);
|
|
Register Rresult = reg_to_register_object($dst$$reg);
|
|
|
|
__ sra(Rdivisor, 0, Rdivisor);
|
|
__ sra(Rdividend, 0, Rdividend);
|
|
__ sdivx(Rdividend, Rdivisor, Rresult);
|
|
%}
|
|
|
|
enc_class idiv_imm(iRegIsafe src1, immI13 imm, iRegIsafe dst) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
Register Rdividend = reg_to_register_object($src1$$reg);
|
|
int divisor = $imm$$constant;
|
|
Register Rresult = reg_to_register_object($dst$$reg);
|
|
|
|
__ sra(Rdividend, 0, Rdividend);
|
|
__ sdivx(Rdividend, divisor, Rresult);
|
|
%}
|
|
|
|
enc_class enc_mul_hi(iRegIsafe dst, iRegIsafe src1, iRegIsafe src2) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
Register Rsrc1 = reg_to_register_object($src1$$reg);
|
|
Register Rsrc2 = reg_to_register_object($src2$$reg);
|
|
Register Rdst = reg_to_register_object($dst$$reg);
|
|
|
|
__ sra( Rsrc1, 0, Rsrc1 );
|
|
__ sra( Rsrc2, 0, Rsrc2 );
|
|
__ mulx( Rsrc1, Rsrc2, Rdst );
|
|
__ srlx( Rdst, 32, Rdst );
|
|
%}
|
|
|
|
enc_class irem_reg(iRegIsafe src1, iRegIsafe src2, iRegIsafe dst, o7RegL scratch) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
Register Rdividend = reg_to_register_object($src1$$reg);
|
|
Register Rdivisor = reg_to_register_object($src2$$reg);
|
|
Register Rresult = reg_to_register_object($dst$$reg);
|
|
Register Rscratch = reg_to_register_object($scratch$$reg);
|
|
|
|
assert(Rdividend != Rscratch, "");
|
|
assert(Rdivisor != Rscratch, "");
|
|
|
|
__ sra(Rdividend, 0, Rdividend);
|
|
__ sra(Rdivisor, 0, Rdivisor);
|
|
__ sdivx(Rdividend, Rdivisor, Rscratch);
|
|
__ mulx(Rscratch, Rdivisor, Rscratch);
|
|
__ sub(Rdividend, Rscratch, Rresult);
|
|
%}
|
|
|
|
enc_class irem_imm(iRegIsafe src1, immI13 imm, iRegIsafe dst, o7RegL scratch) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
Register Rdividend = reg_to_register_object($src1$$reg);
|
|
int divisor = $imm$$constant;
|
|
Register Rresult = reg_to_register_object($dst$$reg);
|
|
Register Rscratch = reg_to_register_object($scratch$$reg);
|
|
|
|
assert(Rdividend != Rscratch, "");
|
|
|
|
__ sra(Rdividend, 0, Rdividend);
|
|
__ sdivx(Rdividend, divisor, Rscratch);
|
|
__ mulx(Rscratch, divisor, Rscratch);
|
|
__ sub(Rdividend, Rscratch, Rresult);
|
|
%}
|
|
|
|
enc_class fabss (sflt_reg dst, sflt_reg src) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Fdst = reg_to_SingleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fsrc = reg_to_SingleFloatRegister_object($src$$reg);
|
|
|
|
__ fabs(FloatRegisterImpl::S, Fsrc, Fdst);
|
|
%}
|
|
|
|
enc_class fabsd (dflt_reg dst, dflt_reg src) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Fdst = reg_to_DoubleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fsrc = reg_to_DoubleFloatRegister_object($src$$reg);
|
|
|
|
__ fabs(FloatRegisterImpl::D, Fsrc, Fdst);
|
|
%}
|
|
|
|
enc_class fnegd (dflt_reg dst, dflt_reg src) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Fdst = reg_to_DoubleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fsrc = reg_to_DoubleFloatRegister_object($src$$reg);
|
|
|
|
__ fneg(FloatRegisterImpl::D, Fsrc, Fdst);
|
|
%}
|
|
|
|
enc_class fsqrts (sflt_reg dst, sflt_reg src) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Fdst = reg_to_SingleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fsrc = reg_to_SingleFloatRegister_object($src$$reg);
|
|
|
|
__ fsqrt(FloatRegisterImpl::S, Fsrc, Fdst);
|
|
%}
|
|
|
|
enc_class fsqrtd (dflt_reg dst, dflt_reg src) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Fdst = reg_to_DoubleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fsrc = reg_to_DoubleFloatRegister_object($src$$reg);
|
|
|
|
__ fsqrt(FloatRegisterImpl::D, Fsrc, Fdst);
|
|
%}
|
|
|
|
|
|
enc_class fmadds (sflt_reg dst, sflt_reg a, sflt_reg b, sflt_reg c) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Frd = reg_to_SingleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fra = reg_to_SingleFloatRegister_object($a$$reg);
|
|
FloatRegister Frb = reg_to_SingleFloatRegister_object($b$$reg);
|
|
FloatRegister Frc = reg_to_SingleFloatRegister_object($c$$reg);
|
|
|
|
__ fmadd(FloatRegisterImpl::S, Fra, Frb, Frc, Frd);
|
|
%}
|
|
|
|
enc_class fmaddd (dflt_reg dst, dflt_reg a, dflt_reg b, dflt_reg c) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Frd = reg_to_DoubleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fra = reg_to_DoubleFloatRegister_object($a$$reg);
|
|
FloatRegister Frb = reg_to_DoubleFloatRegister_object($b$$reg);
|
|
FloatRegister Frc = reg_to_DoubleFloatRegister_object($c$$reg);
|
|
|
|
__ fmadd(FloatRegisterImpl::D, Fra, Frb, Frc, Frd);
|
|
%}
|
|
|
|
enc_class fmsubs (sflt_reg dst, sflt_reg a, sflt_reg b, sflt_reg c) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Frd = reg_to_SingleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fra = reg_to_SingleFloatRegister_object($a$$reg);
|
|
FloatRegister Frb = reg_to_SingleFloatRegister_object($b$$reg);
|
|
FloatRegister Frc = reg_to_SingleFloatRegister_object($c$$reg);
|
|
|
|
__ fmsub(FloatRegisterImpl::S, Fra, Frb, Frc, Frd);
|
|
%}
|
|
|
|
enc_class fmsubd (dflt_reg dst, dflt_reg a, dflt_reg b, dflt_reg c) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Frd = reg_to_DoubleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fra = reg_to_DoubleFloatRegister_object($a$$reg);
|
|
FloatRegister Frb = reg_to_DoubleFloatRegister_object($b$$reg);
|
|
FloatRegister Frc = reg_to_DoubleFloatRegister_object($c$$reg);
|
|
|
|
__ fmsub(FloatRegisterImpl::D, Fra, Frb, Frc, Frd);
|
|
%}
|
|
|
|
enc_class fnmadds (sflt_reg dst, sflt_reg a, sflt_reg b, sflt_reg c) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Frd = reg_to_SingleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fra = reg_to_SingleFloatRegister_object($a$$reg);
|
|
FloatRegister Frb = reg_to_SingleFloatRegister_object($b$$reg);
|
|
FloatRegister Frc = reg_to_SingleFloatRegister_object($c$$reg);
|
|
|
|
__ fnmadd(FloatRegisterImpl::S, Fra, Frb, Frc, Frd);
|
|
%}
|
|
|
|
enc_class fnmaddd (dflt_reg dst, dflt_reg a, dflt_reg b, dflt_reg c) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Frd = reg_to_DoubleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fra = reg_to_DoubleFloatRegister_object($a$$reg);
|
|
FloatRegister Frb = reg_to_DoubleFloatRegister_object($b$$reg);
|
|
FloatRegister Frc = reg_to_DoubleFloatRegister_object($c$$reg);
|
|
|
|
__ fnmadd(FloatRegisterImpl::D, Fra, Frb, Frc, Frd);
|
|
%}
|
|
|
|
enc_class fnmsubs (sflt_reg dst, sflt_reg a, sflt_reg b, sflt_reg c) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Frd = reg_to_SingleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fra = reg_to_SingleFloatRegister_object($a$$reg);
|
|
FloatRegister Frb = reg_to_SingleFloatRegister_object($b$$reg);
|
|
FloatRegister Frc = reg_to_SingleFloatRegister_object($c$$reg);
|
|
|
|
__ fnmsub(FloatRegisterImpl::S, Fra, Frb, Frc, Frd);
|
|
%}
|
|
|
|
enc_class fnmsubd (dflt_reg dst, dflt_reg a, dflt_reg b, dflt_reg c) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Frd = reg_to_DoubleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fra = reg_to_DoubleFloatRegister_object($a$$reg);
|
|
FloatRegister Frb = reg_to_DoubleFloatRegister_object($b$$reg);
|
|
FloatRegister Frc = reg_to_DoubleFloatRegister_object($c$$reg);
|
|
|
|
__ fnmsub(FloatRegisterImpl::D, Fra, Frb, Frc, Frd);
|
|
%}
|
|
|
|
|
|
enc_class fmovs (dflt_reg dst, dflt_reg src) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Fdst = reg_to_SingleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fsrc = reg_to_SingleFloatRegister_object($src$$reg);
|
|
|
|
__ fmov(FloatRegisterImpl::S, Fsrc, Fdst);
|
|
%}
|
|
|
|
enc_class fmovd (dflt_reg dst, dflt_reg src) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
FloatRegister Fdst = reg_to_DoubleFloatRegister_object($dst$$reg);
|
|
FloatRegister Fsrc = reg_to_DoubleFloatRegister_object($src$$reg);
|
|
|
|
__ fmov(FloatRegisterImpl::D, Fsrc, Fdst);
|
|
%}
|
|
|
|
enc_class Fast_Lock(iRegP oop, iRegP box, o7RegP scratch, iRegP scratch2) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
Register Roop = reg_to_register_object($oop$$reg);
|
|
Register Rbox = reg_to_register_object($box$$reg);
|
|
Register Rscratch = reg_to_register_object($scratch$$reg);
|
|
Register Rmark = reg_to_register_object($scratch2$$reg);
|
|
|
|
assert(Roop != Rscratch, "");
|
|
assert(Roop != Rmark, "");
|
|
assert(Rbox != Rscratch, "");
|
|
assert(Rbox != Rmark, "");
|
|
|
|
__ compiler_lock_object(Roop, Rmark, Rbox, Rscratch, _counters, UseBiasedLocking && !UseOptoBiasInlining);
|
|
%}
|
|
|
|
enc_class Fast_Unlock(iRegP oop, iRegP box, o7RegP scratch, iRegP scratch2) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
|
|
Register Roop = reg_to_register_object($oop$$reg);
|
|
Register Rbox = reg_to_register_object($box$$reg);
|
|
Register Rscratch = reg_to_register_object($scratch$$reg);
|
|
Register Rmark = reg_to_register_object($scratch2$$reg);
|
|
|
|
assert(Roop != Rscratch, "");
|
|
assert(Roop != Rmark, "");
|
|
assert(Rbox != Rscratch, "");
|
|
assert(Rbox != Rmark, "");
|
|
|
|
__ compiler_unlock_object(Roop, Rmark, Rbox, Rscratch, UseBiasedLocking && !UseOptoBiasInlining);
|
|
%}
|
|
|
|
enc_class enc_cas( iRegP mem, iRegP old, iRegP new ) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
Register Rmem = reg_to_register_object($mem$$reg);
|
|
Register Rold = reg_to_register_object($old$$reg);
|
|
Register Rnew = reg_to_register_object($new$$reg);
|
|
|
|
__ cas_ptr(Rmem, Rold, Rnew); // Swap(*Rmem,Rnew) if *Rmem == Rold
|
|
__ cmp( Rold, Rnew );
|
|
%}
|
|
|
|
enc_class enc_casx( iRegP mem, iRegL old, iRegL new) %{
|
|
Register Rmem = reg_to_register_object($mem$$reg);
|
|
Register Rold = reg_to_register_object($old$$reg);
|
|
Register Rnew = reg_to_register_object($new$$reg);
|
|
|
|
MacroAssembler _masm(&cbuf);
|
|
__ mov(Rnew, O7);
|
|
__ casx(Rmem, Rold, O7);
|
|
__ cmp( Rold, O7 );
|
|
%}
|
|
|
|
// raw int cas, used for compareAndSwap
|
|
enc_class enc_casi( iRegP mem, iRegL old, iRegL new) %{
|
|
Register Rmem = reg_to_register_object($mem$$reg);
|
|
Register Rold = reg_to_register_object($old$$reg);
|
|
Register Rnew = reg_to_register_object($new$$reg);
|
|
|
|
MacroAssembler _masm(&cbuf);
|
|
__ mov(Rnew, O7);
|
|
__ cas(Rmem, Rold, O7);
|
|
__ cmp( Rold, O7 );
|
|
%}
|
|
|
|
// raw int cas without using tmp register for compareAndExchange
|
|
enc_class enc_casi_exch( iRegP mem, iRegL old, iRegL new) %{
|
|
Register Rmem = reg_to_register_object($mem$$reg);
|
|
Register Rold = reg_to_register_object($old$$reg);
|
|
Register Rnew = reg_to_register_object($new$$reg);
|
|
|
|
MacroAssembler _masm(&cbuf);
|
|
__ cas(Rmem, Rold, Rnew);
|
|
%}
|
|
|
|
// 64-bit cas without using tmp register for compareAndExchange
|
|
enc_class enc_casx_exch( iRegP mem, iRegL old, iRegL new) %{
|
|
Register Rmem = reg_to_register_object($mem$$reg);
|
|
Register Rold = reg_to_register_object($old$$reg);
|
|
Register Rnew = reg_to_register_object($new$$reg);
|
|
|
|
MacroAssembler _masm(&cbuf);
|
|
__ casx(Rmem, Rold, Rnew);
|
|
%}
|
|
|
|
enc_class enc_lflags_ne_to_boolean( iRegI res ) %{
|
|
Register Rres = reg_to_register_object($res$$reg);
|
|
|
|
MacroAssembler _masm(&cbuf);
|
|
__ mov(1, Rres);
|
|
__ movcc( Assembler::notEqual, false, Assembler::xcc, G0, Rres );
|
|
%}
|
|
|
|
enc_class enc_iflags_ne_to_boolean( iRegI res ) %{
|
|
Register Rres = reg_to_register_object($res$$reg);
|
|
|
|
MacroAssembler _masm(&cbuf);
|
|
__ mov(1, Rres);
|
|
__ movcc( Assembler::notEqual, false, Assembler::icc, G0, Rres );
|
|
%}
|
|
|
|
enc_class floating_cmp ( iRegP dst, regF src1, regF src2 ) %{
|
|
MacroAssembler _masm(&cbuf);
|
|
Register Rdst = reg_to_register_object($dst$$reg);
|
|
FloatRegister Fsrc1 = $primary ? reg_to_SingleFloatRegister_object($src1$$reg)
|
|
: reg_to_DoubleFloatRegister_object($src1$$reg);
|
|
FloatRegister Fsrc2 = $primary ? reg_to_SingleFloatRegister_object($src2$$reg)
|
|
: reg_to_DoubleFloatRegister_object($src2$$reg);
|
|
|
|
// Convert condition code fcc0 into -1,0,1; unordered reports less-than (-1)
|
|
__ float_cmp( $primary, -1, Fsrc1, Fsrc2, Rdst);
|
|
%}
|
|
|
|
enc_class enc_rethrow() %{
|
|
cbuf.set_insts_mark();
|
|
Register temp_reg = G3;
|
|
AddressLiteral rethrow_stub(OptoRuntime::rethrow_stub());
|
|
assert(temp_reg != reg_to_register_object(R_I0_num), "temp must not break oop_reg");
|
|
MacroAssembler _masm(&cbuf);
|
|
#ifdef ASSERT
|
|
__ save_frame(0);
|
|
AddressLiteral last_rethrow_addrlit(&last_rethrow);
|
|
__ sethi(last_rethrow_addrlit, L1);
|
|
Address addr(L1, last_rethrow_addrlit.low10());
|
|
__ rdpc(L2);
|
|
__ inc(L2, 3 * BytesPerInstWord); // skip this & 2 more insns to point at jump_to
|
|
__ st_ptr(L2, addr);
|
|
__ restore();
|
|
#endif
|
|
__ JUMP(rethrow_stub, temp_reg, 0); // sethi;jmp
|
|
__ delayed()->nop();
|
|
%}
|
|
|
|
enc_class emit_mem_nop() %{
|
|
// Generates the instruction LDUXA [o6,g0],#0x82,g0
|
|
cbuf.insts()->emit_int32((unsigned int) 0xc0839040);
|
|
%}
|
|
|
|
enc_class emit_fadd_nop() %{
|
|
// Generates the instruction FMOVS f31,f31
|
|
cbuf.insts()->emit_int32((unsigned int) 0xbfa0003f);
|
|
%}
|
|
|
|
enc_class emit_br_nop() %{
|
|
// Generates the instruction BPN,PN .
|
|
cbuf.insts()->emit_int32((unsigned int) 0x00400000);
|
|
%}
|
|
|
|
enc_class enc_membar_acquire %{
|
|
MacroAssembler _masm(&cbuf);
|
|
__ membar( Assembler::Membar_mask_bits(Assembler::LoadStore | Assembler::LoadLoad) );
|
|
%}
|
|
|
|
enc_class enc_membar_release %{
|
|
MacroAssembler _masm(&cbuf);
|
|
__ membar( Assembler::Membar_mask_bits(Assembler::LoadStore | Assembler::StoreStore) );
|
|
%}
|
|
|
|
enc_class enc_membar_volatile %{
|
|
MacroAssembler _masm(&cbuf);
|
|
__ membar( Assembler::Membar_mask_bits(Assembler::StoreLoad) );
|
|
%}
|
|
|
|
%}
|
|
|
|
//----------FRAME--------------------------------------------------------------
|
|
// Definition of frame structure and management information.
|
|
//
|
|
// S T A C K L A Y O U T Allocators stack-slot number
|
|
// | (to get allocators register number
|
|
// G Owned by | | v add VMRegImpl::stack0)
|
|
// r CALLER | |
|
|
// o | +--------+ pad to even-align allocators stack-slot
|
|
// w V | pad0 | numbers; owned by CALLER
|
|
// t -----------+--------+----> Matcher::_in_arg_limit, unaligned
|
|
// h ^ | in | 5
|
|
// | | args | 4 Holes in incoming args owned by SELF
|
|
// | | | | 3
|
|
// | | +--------+
|
|
// V | | old out| Empty on Intel, window on Sparc
|
|
// | old |preserve| Must be even aligned.
|
|
// | SP-+--------+----> Matcher::_old_SP, 8 (or 16 in LP64)-byte aligned
|
|
// | | in | 3 area for Intel ret address
|
|
// Owned by |preserve| Empty on Sparc.
|
|
// SELF +--------+
|
|
// | | pad2 | 2 pad to align old SP
|
|
// | +--------+ 1
|
|
// | | locks | 0
|
|
// | +--------+----> VMRegImpl::stack0, 8 (or 16 in LP64)-byte aligned
|
|
// | | pad1 | 11 pad to align new SP
|
|
// | +--------+
|
|
// | | | 10
|
|
// | | spills | 9 spills
|
|
// V | | 8 (pad0 slot for callee)
|
|
// -----------+--------+----> Matcher::_out_arg_limit, unaligned
|
|
// ^ | out | 7
|
|
// | | args | 6 Holes in outgoing args owned by CALLEE
|
|
// Owned by +--------+
|
|
// CALLEE | new out| 6 Empty on Intel, window on Sparc
|
|
// | new |preserve| Must be even-aligned.
|
|
// | SP-+--------+----> Matcher::_new_SP, even aligned
|
|
// | | |
|
|
//
|
|
// Note 1: Only region 8-11 is determined by the allocator. Region 0-5 is
|
|
// known from SELF's arguments and the Java calling convention.
|
|
// Region 6-7 is determined per call site.
|
|
// Note 2: If the calling convention leaves holes in the incoming argument
|
|
// area, those holes are owned by SELF. Holes in the outgoing area
|
|
// are owned by the CALLEE. Holes should not be nessecary in the
|
|
// incoming area, as the Java calling convention is completely under
|
|
// the control of the AD file. Doubles can be sorted and packed to
|
|
// avoid holes. Holes in the outgoing arguments may be necessary for
|
|
// varargs C calling conventions.
|
|
// Note 3: Region 0-3 is even aligned, with pad2 as needed. Region 3-5 is
|
|
// even aligned with pad0 as needed.
|
|
// Region 6 is even aligned. Region 6-7 is NOT even aligned;
|
|
// region 6-11 is even aligned; it may be padded out more so that
|
|
// the region from SP to FP meets the minimum stack alignment.
|
|
|
|
frame %{
|
|
// What direction does stack grow in (assumed to be same for native & Java)
|
|
stack_direction(TOWARDS_LOW);
|
|
|
|
// These two registers define part of the calling convention
|
|
// between compiled code and the interpreter.
|
|
inline_cache_reg(R_G5); // Inline Cache Register or Method* for I2C
|
|
interpreter_method_oop_reg(R_G5); // Method Oop Register when calling interpreter
|
|
|
|
// Optional: name the operand used by cisc-spilling to access [stack_pointer + offset]
|
|
cisc_spilling_operand_name(indOffset);
|
|
|
|
// Number of stack slots consumed by a Monitor enter
|
|
sync_stack_slots(2);
|
|
|
|
// Compiled code's Frame Pointer
|
|
frame_pointer(R_SP);
|
|
|
|
// Stack alignment requirement
|
|
stack_alignment(StackAlignmentInBytes);
|
|
// LP64: Alignment size in bytes (128-bit -> 16 bytes)
|
|
// !LP64: Alignment size in bytes (64-bit -> 8 bytes)
|
|
|
|
// Number of stack slots between incoming argument block and the start of
|
|
// a new frame. The PROLOG must add this many slots to the stack. The
|
|
// EPILOG must remove this many slots.
|
|
in_preserve_stack_slots(0);
|
|
|
|
// Number of outgoing stack slots killed above the out_preserve_stack_slots
|
|
// for calls to C. Supports the var-args backing area for register parms.
|
|
// ADLC doesn't support parsing expressions, so I folded the math by hand.
|
|
// (callee_register_argument_save_area_words (6) + callee_aggregate_return_pointer_words (0)) * 2-stack-slots-per-word
|
|
varargs_C_out_slots_killed(12);
|
|
|
|
// The after-PROLOG location of the return address. Location of
|
|
// return address specifies a type (REG or STACK) and a number
|
|
// representing the register number (i.e. - use a register name) or
|
|
// stack slot.
|
|
return_addr(REG R_I7); // Ret Addr is in register I7
|
|
|
|
// Body of function which returns an OptoRegs array locating
|
|
// arguments either in registers or in stack slots for calling
|
|
// java
|
|
calling_convention %{
|
|
(void) SharedRuntime::java_calling_convention(sig_bt, regs, length, is_outgoing);
|
|
|
|
%}
|
|
|
|
// Body of function which returns an OptoRegs array locating
|
|
// arguments either in registers or in stack slots for calling
|
|
// C.
|
|
c_calling_convention %{
|
|
// This is obviously always outgoing
|
|
(void) SharedRuntime::c_calling_convention(sig_bt, regs, /*regs2=*/NULL, length);
|
|
%}
|
|
|
|
// Location of native (C/C++) and interpreter return values. This is specified to
|
|
// be the same as Java. In the 32-bit VM, long values are actually returned from
|
|
// native calls in O0:O1 and returned to the interpreter in I0:I1. The copying
|
|
// to and from the register pairs is done by the appropriate call and epilog
|
|
// opcodes. This simplifies the register allocator.
|
|
c_return_value %{
|
|
assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" );
|
|
static int lo_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_O0_num, R_O0_num, R_O0_num, R_F0_num, R_F0_num, R_O0_num };
|
|
static int hi_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_O0H_num, OptoReg::Bad, R_F1_num, R_O0H_num};
|
|
static int lo_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_I0_num, R_I0_num, R_I0_num, R_F0_num, R_F0_num, R_I0_num };
|
|
static int hi_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_I0H_num, OptoReg::Bad, R_F1_num, R_I0H_num};
|
|
return OptoRegPair( (is_outgoing?hi_out:hi_in)[ideal_reg],
|
|
(is_outgoing?lo_out:lo_in)[ideal_reg] );
|
|
%}
|
|
|
|
// Location of compiled Java return values. Same as C
|
|
return_value %{
|
|
assert( ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, "only return normal values" );
|
|
static int lo_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_O0_num, R_O0_num, R_O0_num, R_F0_num, R_F0_num, R_O0_num };
|
|
static int hi_out[Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_O0H_num, OptoReg::Bad, R_F1_num, R_O0H_num};
|
|
static int lo_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, R_I0_num, R_I0_num, R_I0_num, R_F0_num, R_F0_num, R_I0_num };
|
|
static int hi_in [Op_RegL+1] = { OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, OptoReg::Bad, R_I0H_num, OptoReg::Bad, R_F1_num, R_I0H_num};
|
|
return OptoRegPair( (is_outgoing?hi_out:hi_in)[ideal_reg],
|
|
(is_outgoing?lo_out:lo_in)[ideal_reg] );
|
|
%}
|
|
|
|
%}
|
|
|
|
|
|
//----------ATTRIBUTES---------------------------------------------------------
|
|
//----------Operand Attributes-------------------------------------------------
|
|
op_attrib op_cost(1); // Required cost attribute
|
|
|
|
//----------Instruction Attributes---------------------------------------------
|
|
ins_attrib ins_cost(DEFAULT_COST); // Required cost attribute
|
|
ins_attrib ins_size(32); // Required size attribute (in bits)
|
|
|
|
// avoid_back_to_back attribute is an expression that must return
|
|
// one of the following values defined in MachNode:
|
|
// AVOID_NONE - instruction can be placed anywhere
|
|
// AVOID_BEFORE - instruction cannot be placed after an
|
|
// instruction with MachNode::AVOID_AFTER
|
|
// AVOID_AFTER - the next instruction cannot be the one
|
|
// with MachNode::AVOID_BEFORE
|
|
// AVOID_BEFORE_AND_AFTER - BEFORE and AFTER attributes at
|
|
// the same time
|
|
ins_attrib ins_avoid_back_to_back(MachNode::AVOID_NONE);
|
|
|
|
ins_attrib ins_short_branch(0); // Required flag: is this instruction a
|
|
// non-matching short branch variant of some
|
|
// long branch?
|
|
|
|
//----------OPERANDS-----------------------------------------------------------
|
|
// Operand definitions must precede instruction definitions for correct parsing
|
|
// in the ADLC because operands constitute user defined types which are used in
|
|
// instruction definitions.
|
|
|
|
//----------Simple Operands----------------------------------------------------
|
|
// Immediate Operands
|
|
// Integer Immediate: 32-bit
|
|
operand immI() %{
|
|
match(ConI);
|
|
|
|
op_cost(0);
|
|
// formats are generated automatically for constants and base registers
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: 0-bit
|
|
operand immI0() %{
|
|
predicate(n->get_int() == 0);
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: 5-bit
|
|
operand immI5() %{
|
|
predicate(Assembler::is_simm5(n->get_int()));
|
|
match(ConI);
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: 8-bit
|
|
operand immI8() %{
|
|
predicate(Assembler::is_simm8(n->get_int()));
|
|
match(ConI);
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: the value 10
|
|
operand immI10() %{
|
|
predicate(n->get_int() == 10);
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: 11-bit
|
|
operand immI11() %{
|
|
predicate(Assembler::is_simm11(n->get_int()));
|
|
match(ConI);
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: 13-bit
|
|
operand immI13() %{
|
|
predicate(Assembler::is_simm13(n->get_int()));
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: 13-bit minus 7
|
|
operand immI13m7() %{
|
|
predicate((-4096 < n->get_int()) && ((n->get_int() + 7) <= 4095));
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: 16-bit
|
|
operand immI16() %{
|
|
predicate(Assembler::is_simm16(n->get_int()));
|
|
match(ConI);
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: the values 1-31
|
|
operand immI_1_31() %{
|
|
predicate(n->get_int() >= 1 && n->get_int() <= 31);
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: the values 32-63
|
|
operand immI_32_63() %{
|
|
predicate(n->get_int() >= 32 && n->get_int() <= 63);
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Immediates for special shifts (sign extend)
|
|
|
|
// Integer Immediate: the value 16
|
|
operand immI_16() %{
|
|
predicate(n->get_int() == 16);
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: the value 24
|
|
operand immI_24() %{
|
|
predicate(n->get_int() == 24);
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
// Integer Immediate: the value 255
|
|
operand immI_255() %{
|
|
predicate( n->get_int() == 255 );
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: the value 65535
|
|
operand immI_65535() %{
|
|
predicate(n->get_int() == 65535);
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: the values 0-31
|
|
operand immU5() %{
|
|
predicate(n->get_int() >= 0 && n->get_int() <= 31);
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: 6-bit
|
|
operand immU6() %{
|
|
predicate(n->get_int() >= 0 && n->get_int() <= 63);
|
|
match(ConI);
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Unsigned Integer Immediate: 12-bit (non-negative that fits in simm13)
|
|
operand immU12() %{
|
|
predicate((0 <= n->get_int()) && Assembler::is_simm13(n->get_int()));
|
|
match(ConI);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Unsigned Long Immediate: 12-bit (non-negative that fits in simm13)
|
|
operand immUL12() %{
|
|
predicate((0 <= n->get_long()) && (n->get_long() == (int)n->get_long()) && Assembler::is_simm13((int)n->get_long()));
|
|
match(ConL);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate non-negative
|
|
operand immU31()
|
|
%{
|
|
predicate(n->get_int() >= 0);
|
|
match(ConI);
|
|
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Long Immediate: the value FF
|
|
operand immL_FF() %{
|
|
predicate( n->get_long() == 0xFFL );
|
|
match(ConL);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Long Immediate: the value FFFF
|
|
operand immL_FFFF() %{
|
|
predicate( n->get_long() == 0xFFFFL );
|
|
match(ConL);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Pointer Immediate: 32 or 64-bit
|
|
operand immP() %{
|
|
match(ConP);
|
|
|
|
op_cost(5);
|
|
// formats are generated automatically for constants and base registers
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Pointer Immediate: 64-bit
|
|
operand immP_set() %{
|
|
predicate(!VM_Version::has_fast_ld());
|
|
match(ConP);
|
|
|
|
op_cost(5);
|
|
// formats are generated automatically for constants and base registers
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Pointer Immediate: 64-bit
|
|
// From Niagara2 processors on a load should be better than materializing.
|
|
operand immP_load() %{
|
|
predicate(VM_Version::has_fast_ld() && (n->bottom_type()->isa_oop_ptr() || (MacroAssembler::insts_for_set(n->get_ptr()) > 3)));
|
|
match(ConP);
|
|
|
|
op_cost(5);
|
|
// formats are generated automatically for constants and base registers
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Pointer Immediate: 64-bit
|
|
operand immP_no_oop_cheap() %{
|
|
predicate(VM_Version::has_fast_ld() && !n->bottom_type()->isa_oop_ptr() && (MacroAssembler::insts_for_set(n->get_ptr()) <= 3));
|
|
match(ConP);
|
|
|
|
op_cost(5);
|
|
// formats are generated automatically for constants and base registers
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
operand immP13() %{
|
|
predicate((-4096 < n->get_ptr()) && (n->get_ptr() <= 4095));
|
|
match(ConP);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
operand immP0() %{
|
|
predicate(n->get_ptr() == 0);
|
|
match(ConP);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
operand immP_poll() %{
|
|
predicate(n->get_ptr() != 0 && n->get_ptr() == (intptr_t)os::get_polling_page());
|
|
match(ConP);
|
|
|
|
// formats are generated automatically for constants and base registers
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Pointer Immediate
|
|
operand immN()
|
|
%{
|
|
match(ConN);
|
|
|
|
op_cost(10);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
operand immNKlass()
|
|
%{
|
|
match(ConNKlass);
|
|
|
|
op_cost(10);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// NULL Pointer Immediate
|
|
operand immN0()
|
|
%{
|
|
predicate(n->get_narrowcon() == 0);
|
|
match(ConN);
|
|
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
operand immL() %{
|
|
match(ConL);
|
|
op_cost(40);
|
|
// formats are generated automatically for constants and base registers
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
operand immL0() %{
|
|
predicate(n->get_long() == 0L);
|
|
match(ConL);
|
|
op_cost(0);
|
|
// formats are generated automatically for constants and base registers
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Immediate: 5-bit
|
|
operand immL5() %{
|
|
predicate(n->get_long() == (int)n->get_long() && Assembler::is_simm5((int)n->get_long()));
|
|
match(ConL);
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Long Immediate: 13-bit
|
|
operand immL13() %{
|
|
predicate((-4096L < n->get_long()) && (n->get_long() <= 4095L));
|
|
match(ConL);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Long Immediate: 13-bit minus 7
|
|
operand immL13m7() %{
|
|
predicate((-4096L < n->get_long()) && ((n->get_long() + 7L) <= 4095L));
|
|
match(ConL);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Long Immediate: low 32-bit mask
|
|
operand immL_32bits() %{
|
|
predicate(n->get_long() == 0xFFFFFFFFL);
|
|
match(ConL);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Long Immediate: cheap (materialize in <= 3 instructions)
|
|
operand immL_cheap() %{
|
|
predicate(!VM_Version::has_fast_ld() || MacroAssembler::insts_for_set64(n->get_long()) <= 3);
|
|
match(ConL);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Long Immediate: expensive (materialize in > 3 instructions)
|
|
operand immL_expensive() %{
|
|
predicate(VM_Version::has_fast_ld() && MacroAssembler::insts_for_set64(n->get_long()) > 3);
|
|
match(ConL);
|
|
op_cost(0);
|
|
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Double Immediate
|
|
operand immD() %{
|
|
match(ConD);
|
|
|
|
op_cost(40);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Double Immediate: +0.0d
|
|
operand immD0() %{
|
|
predicate(jlong_cast(n->getd()) == 0);
|
|
match(ConD);
|
|
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Float Immediate
|
|
operand immF() %{
|
|
match(ConF);
|
|
|
|
op_cost(20);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Float Immediate: +0.0f
|
|
operand immF0() %{
|
|
predicate(jint_cast(n->getf()) == 0);
|
|
match(ConF);
|
|
|
|
op_cost(0);
|
|
format %{ %}
|
|
interface(CONST_INTER);
|
|
%}
|
|
|
|
// Integer Register Operands
|
|
// Integer Register
|
|
operand iRegI() %{
|
|
constraint(ALLOC_IN_RC(int_reg));
|
|
match(RegI);
|
|
|
|
match(notemp_iRegI);
|
|
match(g1RegI);
|
|
match(o0RegI);
|
|
match(iRegIsafe);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand notemp_iRegI() %{
|
|
constraint(ALLOC_IN_RC(notemp_int_reg));
|
|
match(RegI);
|
|
|
|
match(o0RegI);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand o0RegI() %{
|
|
constraint(ALLOC_IN_RC(o0_regI));
|
|
match(iRegI);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Pointer Register
|
|
operand iRegP() %{
|
|
constraint(ALLOC_IN_RC(ptr_reg));
|
|
match(RegP);
|
|
|
|
match(lock_ptr_RegP);
|
|
match(g1RegP);
|
|
match(g2RegP);
|
|
match(g3RegP);
|
|
match(g4RegP);
|
|
match(i0RegP);
|
|
match(o0RegP);
|
|
match(o1RegP);
|
|
match(l7RegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand sp_ptr_RegP() %{
|
|
constraint(ALLOC_IN_RC(sp_ptr_reg));
|
|
match(RegP);
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand lock_ptr_RegP() %{
|
|
constraint(ALLOC_IN_RC(lock_ptr_reg));
|
|
match(RegP);
|
|
match(i0RegP);
|
|
match(o0RegP);
|
|
match(o1RegP);
|
|
match(l7RegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand g1RegP() %{
|
|
constraint(ALLOC_IN_RC(g1_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand g2RegP() %{
|
|
constraint(ALLOC_IN_RC(g2_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand g3RegP() %{
|
|
constraint(ALLOC_IN_RC(g3_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand g1RegI() %{
|
|
constraint(ALLOC_IN_RC(g1_regI));
|
|
match(iRegI);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand g3RegI() %{
|
|
constraint(ALLOC_IN_RC(g3_regI));
|
|
match(iRegI);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand g4RegI() %{
|
|
constraint(ALLOC_IN_RC(g4_regI));
|
|
match(iRegI);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand g4RegP() %{
|
|
constraint(ALLOC_IN_RC(g4_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand i0RegP() %{
|
|
constraint(ALLOC_IN_RC(i0_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand o0RegP() %{
|
|
constraint(ALLOC_IN_RC(o0_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand o1RegP() %{
|
|
constraint(ALLOC_IN_RC(o1_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand o2RegP() %{
|
|
constraint(ALLOC_IN_RC(o2_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand o7RegP() %{
|
|
constraint(ALLOC_IN_RC(o7_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand l7RegP() %{
|
|
constraint(ALLOC_IN_RC(l7_regP));
|
|
match(iRegP);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand o7RegI() %{
|
|
constraint(ALLOC_IN_RC(o7_regI));
|
|
match(iRegI);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand iRegN() %{
|
|
constraint(ALLOC_IN_RC(int_reg));
|
|
match(RegN);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Long Register
|
|
operand iRegL() %{
|
|
constraint(ALLOC_IN_RC(long_reg));
|
|
match(RegL);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand o2RegL() %{
|
|
constraint(ALLOC_IN_RC(o2_regL));
|
|
match(iRegL);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand o7RegL() %{
|
|
constraint(ALLOC_IN_RC(o7_regL));
|
|
match(iRegL);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand g1RegL() %{
|
|
constraint(ALLOC_IN_RC(g1_regL));
|
|
match(iRegL);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand g3RegL() %{
|
|
constraint(ALLOC_IN_RC(g3_regL));
|
|
match(iRegL);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Int Register safe
|
|
// This is 64bit safe
|
|
operand iRegIsafe() %{
|
|
constraint(ALLOC_IN_RC(long_reg));
|
|
|
|
match(iRegI);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Condition Code Flag Register
|
|
operand flagsReg() %{
|
|
constraint(ALLOC_IN_RC(int_flags));
|
|
match(RegFlags);
|
|
|
|
format %{ "ccr" %} // both ICC and XCC
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Condition Code Register, unsigned comparisons.
|
|
operand flagsRegU() %{
|
|
constraint(ALLOC_IN_RC(int_flags));
|
|
match(RegFlags);
|
|
|
|
format %{ "icc_U" %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Condition Code Register, pointer comparisons.
|
|
operand flagsRegP() %{
|
|
constraint(ALLOC_IN_RC(int_flags));
|
|
match(RegFlags);
|
|
|
|
format %{ "xcc_P" %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Condition Code Register, long comparisons.
|
|
operand flagsRegL() %{
|
|
constraint(ALLOC_IN_RC(int_flags));
|
|
match(RegFlags);
|
|
|
|
format %{ "xcc_L" %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Condition Code Register, unsigned long comparisons.
|
|
operand flagsRegUL() %{
|
|
constraint(ALLOC_IN_RC(int_flags));
|
|
match(RegFlags);
|
|
|
|
format %{ "xcc_UL" %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Condition Code Register, floating comparisons, unordered same as "less".
|
|
operand flagsRegF() %{
|
|
constraint(ALLOC_IN_RC(float_flags));
|
|
match(RegFlags);
|
|
match(flagsRegF0);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand flagsRegF0() %{
|
|
constraint(ALLOC_IN_RC(float_flag0));
|
|
match(RegFlags);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
|
|
// Condition Code Flag Register used by long compare
|
|
operand flagsReg_long_LTGE() %{
|
|
constraint(ALLOC_IN_RC(int_flags));
|
|
match(RegFlags);
|
|
format %{ "icc_LTGE" %}
|
|
interface(REG_INTER);
|
|
%}
|
|
operand flagsReg_long_EQNE() %{
|
|
constraint(ALLOC_IN_RC(int_flags));
|
|
match(RegFlags);
|
|
format %{ "icc_EQNE" %}
|
|
interface(REG_INTER);
|
|
%}
|
|
operand flagsReg_long_LEGT() %{
|
|
constraint(ALLOC_IN_RC(int_flags));
|
|
match(RegFlags);
|
|
format %{ "icc_LEGT" %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
|
|
operand regD() %{
|
|
constraint(ALLOC_IN_RC(dflt_reg));
|
|
match(RegD);
|
|
|
|
match(regD_low);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand regF() %{
|
|
constraint(ALLOC_IN_RC(sflt_reg));
|
|
match(RegF);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand regD_low() %{
|
|
constraint(ALLOC_IN_RC(dflt_low_reg));
|
|
match(regD);
|
|
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
// Special Registers
|
|
|
|
// Method Register
|
|
operand inline_cache_regP(iRegP reg) %{
|
|
constraint(ALLOC_IN_RC(g5_regP)); // G5=inline_cache_reg but uses 2 bits instead of 1
|
|
match(reg);
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
operand interpreter_method_oop_regP(iRegP reg) %{
|
|
constraint(ALLOC_IN_RC(g5_regP)); // G5=interpreter_method_oop_reg but uses 2 bits instead of 1
|
|
match(reg);
|
|
format %{ %}
|
|
interface(REG_INTER);
|
|
%}
|
|
|
|
|
|
//----------Complex Operands---------------------------------------------------
|
|
// Indirect Memory Reference
|
|
operand indirect(sp_ptr_RegP reg) %{
|
|
constraint(ALLOC_IN_RC(sp_ptr_reg));
|
|
match(reg);
|
|
|
|
op_cost(100);
|
|
format %{ "[$reg]" %}
|
|
interface(MEMORY_INTER) %{
|
|
base($reg);
|
|
index(0x0);
|
|
scale(0x0);
|
|
disp(0x0);
|
|
%}
|
|
%}
|
|
|
|
// Indirect with simm13 Offset
|
|
operand indOffset13(sp_ptr_RegP reg, immX13 offset) %{
|
|
constraint(ALLOC_IN_RC(sp_ptr_reg));
|
|
match(AddP reg offset);
|
|
|
|
op_cost(100);
|
|
format %{ "[$reg + $offset]" %}
|
|
interface(MEMORY_INTER) %{
|
|
base($reg);
|
|
index(0x0);
|
|
scale(0x0);
|
|
disp($offset);
|
|
%}
|
|
%}
|
|
|
|
// Indirect with simm13 Offset minus 7
|
|
operand indOffset13m7(sp_ptr_RegP reg, immX13m7 offset) %{
|
|
constraint(ALLOC_IN_RC(sp_ptr_reg));
|
|
match(AddP reg offset);
|
|
|
|
op_cost(100);
|
|
format %{ "[$reg + $offset]" %}
|
|
interface(MEMORY_INTER) %{
|
|
base($reg);
|
|
index(0x0);
|
|
scale(0x0);
|
|
disp($offset);
|
|
%}
|
|
%}
|
|
|
|
// Note: Intel has a swapped version also, like this:
|
|
//operand indOffsetX(iRegI reg, immP offset) %{
|
|
// constraint(ALLOC_IN_RC(int_reg));
|
|
// match(AddP offset reg);
|
|
//
|
|
// op_cost(100);
|
|
// format %{ "[$reg + $offset]" %}
|
|
// interface(MEMORY_INTER) %{
|
|
// base($reg);
|
|
// index(0x0);
|
|
// scale(0x0);
|
|
// disp($offset);
|
|
// %}
|
|
//%}
|
|
//// However, it doesn't make sense for SPARC, since
|
|
// we have no particularly good way to embed oops in
|
|
// single instructions.
|
|
|
|
// Indirect with Register Index
|
|
operand indIndex(iRegP addr, iRegX index) %{
|
|
constraint(ALLOC_IN_RC(ptr_reg));
|
|
match(AddP addr index);
|
|
|
|
op_cost(100);
|
|
format %{ "[$addr + $index]" %}
|
|
interface(MEMORY_INTER) %{
|
|
base($addr);
|
|
index($index);
|
|
scale(0x0);
|
|
disp(0x0);
|
|
%}
|
|
%}
|
|
|
|
//----------Special Memory Operands--------------------------------------------
|
|
// Stack Slot Operand - This operand is used for loading and storing temporary
|
|
// values on the stack where a match requires a value to
|
|
// flow through memory.
|
|
operand stackSlotI(sRegI reg) %{
|
|
constraint(ALLOC_IN_RC(stack_slots));
|
|
op_cost(100);
|
|
//match(RegI);
|
|
format %{ "[$reg]" %}
|
|
interface(MEMORY_INTER) %{
|
|
base(0xE); // R_SP
|
|
index(0x0);
|
|
scale(0x0);
|
|
disp($reg); // Stack Offset
|
|
%}
|
|
%}
|
|
|
|
operand stackSlotP(sRegP reg) %{
|
|
constraint(ALLOC_IN_RC(stack_slots));
|
|
op_cost(100);
|
|
//match(RegP);
|
|
format %{ "[$reg]" %}
|
|
interface(MEMORY_INTER) %{
|
|
base(0xE); // R_SP
|
|
index(0x0);
|
|
scale(0x0);
|
|
disp($reg); // Stack Offset
|
|
%}
|
|
%}
|
|
|
|
operand stackSlotF(sRegF reg) %{
|
|
constraint(ALLOC_IN_RC(stack_slots));
|
|
op_cost(100);
|
|
//match(RegF);
|
|
format %{ "[$reg]" %}
|
|
interface(MEMORY_INTER) %{
|
|
base(0xE); // R_SP
|
|
index(0x0);
|
|
scale(0x0);
|
|
disp($reg); // Stack Offset
|
|
%}
|
|
%}
|
|
operand stackSlotD(sRegD reg) %{
|
|
constraint(ALLOC_IN_RC(stack_slots));
|
|
op_cost(100);
|
|
//match(RegD);
|
|
format %{ "[$reg]" %}
|
|
interface(MEMORY_INTER) %{
|
|
base(0xE); // R_SP
|
|
index(0x0);
|
|
scale(0x0);
|
|
disp($reg); // Stack Offset
|
|
%}
|
|
%}
|
|
operand stackSlotL(sRegL reg) %{
|
|
constraint(ALLOC_IN_RC(stack_slots));
|
|
op_cost(100);
|
|
//match(RegL);
|
|
format %{ "[$reg]" %}
|
|
interface(MEMORY_INTER) %{
|
|
base(0xE); // R_SP
|
|
index(0x0);
|
|
scale(0x0);
|
|
disp($reg); // Stack Offset
|
|
%}
|
|
%}
|
|
|
|
// Operands for expressing Control Flow
|
|
// NOTE: Label is a predefined operand which should not be redefined in
|
|
// the AD file. It is generically handled within the ADLC.
|
|
|
|
//----------Conditional Branch Operands----------------------------------------
|
|
// Comparison Op - This is the operation of the comparison, and is limited to
|
|
// the following set of codes:
|
|
// L (<), LE (<=), G (>), GE (>=), E (==), NE (!=)
|
|
//
|
|
// Other attributes of the comparison, such as unsignedness, are specified
|
|
// by the comparison instruction that sets a condition code flags register.
|
|
// That result is represented by a flags operand whose subtype is appropriate
|
|
// to the unsignedness (etc.) of the comparison.
|
|
//
|
|
// Later, the instruction which matches both the Comparison Op (a Bool) and
|
|
// the flags (produced by the Cmp) specifies the coding of the comparison op
|
|
// by matching a specific subtype of Bool operand below, such as cmpOpU.
|
|
|
|
operand cmpOp() %{
|
|
match(Bool);
|
|
|
|
format %{ "" %}
|
|
interface(COND_INTER) %{
|
|
equal(0x1);
|
|
not_equal(0x9);
|
|
less(0x3);
|
|
greater_equal(0xB);
|
|
less_equal(0x2);
|
|
greater(0xA);
|
|
overflow(0x7);
|
|
no_overflow(0xF);
|
|
%}
|
|
%}
|
|
|
|
// Comparison Op, unsigned
|
|
operand cmpOpU() %{
|
|
match(Bool);
|
|
predicate(n->as_Bool()->_test._test != BoolTest::overflow &&
|
|
n->as_Bool()->_test._test != BoolTest::no_overflow);
|
|
|
|
format %{ "u" %}
|
|
interface(COND_INTER) %{
|
|
equal(0x1);
|
|
not_equal(0x9);
|
|
less(0x5);
|
|
greater_equal(0xD);
|
|
less_equal(0x4);
|
|
greater(0xC);
|
|
overflow(0x7);
|
|
no_overflow(0xF);
|
|
%}
|
|
%}
|
|
|
|
// Comparison Op, pointer (same as unsigned)
|
|
operand cmpOpP() %{
|
|
match(Bool);
|
|
predicate(n->as_Bool()->_test._test != BoolTest::overflow &&
|
|
n->as_Bool()->_test._test != BoolTest::no_overflow);
|
|
|
|
format %{ "p" %}
|
|
interface(COND_INTER) %{
|
|
equal(0x1);
|
|
not_equal(0x9);
|
|
less(0x5);
|
|
greater_equal(0xD);
|
|
less_equal(0x4);
|
|
greater(0xC);
|
|
overflow(0x7);
|
|
no_overflow(0xF);
|
|
%}
|
|
%}
|
|
|
|
// Comparison Op, branch-register encoding
|
|
operand cmpOp_reg() %{
|
|
match(Bool);
|
|
predicate(n->as_Bool()->_test._test != BoolTest::overflow &&
|
|
n->as_Bool()->_test._test != BoolTest::no_overflow);
|
|
|
|
format %{ "" %}
|
|
interface(COND_INTER) %{
|
|
equal (0x1);
|
|
not_equal (0x5);
|
|
less (0x3);
|
|
greater_equal(0x7);
|
|
less_equal (0x2);
|
|
greater (0x6);
|
|
overflow(0x7); // not supported
|
|
no_overflow(0xF); // not supported
|
|
%}
|
|
%}
|
|
|
|
// Comparison Code, floating, unordered same as less
|
|
operand cmpOpF() %{
|
|
match(Bool);
|
|
predicate(n->as_Bool()->_test._test != BoolTest::overflow &&
|
|
n->as_Bool()->_test._test != BoolTest::no_overflow);
|
|
|
|
format %{ "fl" %}
|
|
interface(COND_INTER) %{
|
|
equal(0x9);
|
|
not_equal(0x1);
|
|
less(0x3);
|
|
greater_equal(0xB);
|
|
less_equal(0xE);
|
|
greater(0x6);
|
|
|
|
overflow(0x7); // not supported
|
|
no_overflow(0xF); // not supported
|
|
%}
|
|
%}
|
|
|
|
// Used by long compare
|
|
operand cmpOp_commute() %{
|
|
match(Bool);
|
|
predicate(n->as_Bool()->_test._test != BoolTest::overflow &&
|
|
n->as_Bool()->_test._test != BoolTest::no_overflow);
|
|
|
|
format %{ "" %}
|
|
interface(COND_INTER) %{
|
|
equal(0x1);
|
|
not_equal(0x9);
|
|
less(0xA);
|
|
greater_equal(0x2);
|
|
less_equal(0xB);
|
|
greater(0x3);
|
|
overflow(0x7);
|
|
no_overflow(0xF);
|
|
%}
|
|
%}
|
|
|
|
//----------OPERAND CLASSES----------------------------------------------------
|
|
// Operand Classes are groups of operands that are used to simplify
|
|
// instruction definitions by not requiring the AD writer to specify separate
|
|
// instructions for every form of operand when the instruction accepts
|
|
// multiple operand types with the same basic encoding and format. The classic
|
|
// case of this is memory operands.
|
|
opclass memory( indirect, indOffset13, indIndex );
|
|
opclass indIndexMemory( indIndex );
|
|
|
|
//----------PIPELINE-----------------------------------------------------------
|
|
pipeline %{
|
|
|
|
//----------ATTRIBUTES---------------------------------------------------------
|
|
attributes %{
|
|
fixed_size_instructions; // Fixed size instructions
|
|
branch_has_delay_slot; // Branch has delay slot following
|
|
max_instructions_per_bundle = 4; // Up to 4 instructions per bundle
|
|
instruction_unit_size = 4; // An instruction is 4 bytes long
|
|
instruction_fetch_unit_size = 16; // The processor fetches one line
|
|
instruction_fetch_units = 1; // of 16 bytes
|
|
|
|
// List of nop instructions
|
|
nops( Nop_A0, Nop_A1, Nop_MS, Nop_FA, Nop_BR );
|
|
%}
|
|
|
|
//----------RESOURCES----------------------------------------------------------
|
|
// Resources are the functional units available to the machine
|
|
resources(A0, A1, MS, BR, FA, FM, IDIV, FDIV, IALU = A0 | A1);
|
|
|
|
//----------PIPELINE DESCRIPTION-----------------------------------------------
|
|
// Pipeline Description specifies the stages in the machine's pipeline
|
|
|
|
pipe_desc(A, P, F, B, I, J, S, R, E, C, M, W, X, T, D);
|
|
|
|
//----------PIPELINE CLASSES---------------------------------------------------
|
|
// Pipeline Classes describe the stages in which input and output are
|
|
// referenced by the hardware pipeline.
|
|
|
|
// Integer ALU reg-reg operation
|
|
pipe_class ialu_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-reg long operation
|
|
pipe_class ialu_reg_reg_2(iRegL dst, iRegL src1, iRegL src2) %{
|
|
instruction_count(2);
|
|
dst : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
IALU : R;
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-reg long dependent operation
|
|
pipe_class ialu_reg_reg_2_dep(iRegL dst, iRegL src1, iRegL src2, flagsReg cr) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
cr : E(write);
|
|
IALU : R(2);
|
|
%}
|
|
|
|
// Integer ALU reg-imm operaion
|
|
pipe_class ialu_reg_imm(iRegI dst, iRegI src1, immI13 src2) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
src1 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-reg operation with condition code
|
|
pipe_class ialu_cc_reg_reg(iRegI dst, iRegI src1, iRegI src2, flagsReg cr) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
cr : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-imm operation with condition code
|
|
pipe_class ialu_cc_reg_imm(iRegI dst, iRegI src1, immI13 src2, flagsReg cr) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
cr : E(write);
|
|
src1 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU zero-reg operation
|
|
pipe_class ialu_zero_reg(iRegI dst, immI0 zero, iRegI src2) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
src2 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU zero-reg operation with condition code only
|
|
pipe_class ialu_cconly_zero_reg(flagsReg cr, iRegI src) %{
|
|
single_instruction;
|
|
cr : E(write);
|
|
src : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-reg operation with condition code only
|
|
pipe_class ialu_cconly_reg_reg(flagsReg cr, iRegI src1, iRegI src2) %{
|
|
single_instruction;
|
|
cr : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-imm operation with condition code only
|
|
pipe_class ialu_cconly_reg_imm(flagsReg cr, iRegI src1, immI13 src2) %{
|
|
single_instruction;
|
|
cr : E(write);
|
|
src1 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-reg-zero operation with condition code only
|
|
pipe_class ialu_cconly_reg_reg_zero(flagsReg cr, iRegI src1, iRegI src2, immI0 zero) %{
|
|
single_instruction;
|
|
cr : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-imm-zero operation with condition code only
|
|
pipe_class ialu_cconly_reg_imm_zero(flagsReg cr, iRegI src1, immI13 src2, immI0 zero) %{
|
|
single_instruction;
|
|
cr : E(write);
|
|
src1 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-reg operation with condition code, src1 modified
|
|
pipe_class ialu_cc_rwreg_reg(flagsReg cr, iRegI src1, iRegI src2) %{
|
|
single_instruction;
|
|
cr : E(write);
|
|
src1 : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-imm operation with condition code, src1 modified
|
|
pipe_class ialu_cc_rwreg_imm(flagsReg cr, iRegI src1, immI13 src2) %{
|
|
single_instruction;
|
|
cr : E(write);
|
|
src1 : E(write);
|
|
src1 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
pipe_class cmpL_reg(iRegI dst, iRegL src1, iRegL src2, flagsReg cr ) %{
|
|
multiple_bundles;
|
|
dst : E(write)+4;
|
|
cr : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
IALU : R(3);
|
|
BR : R(2);
|
|
%}
|
|
|
|
// Integer ALU operation
|
|
pipe_class ialu_none(iRegI dst) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg operation
|
|
pipe_class ialu_reg(iRegI dst, iRegI src) %{
|
|
single_instruction; may_have_no_code;
|
|
dst : E(write);
|
|
src : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg conditional operation
|
|
// This instruction has a 1 cycle stall, and cannot execute
|
|
// in the same cycle as the instruction setting the condition
|
|
// code. We kludge this by pretending to read the condition code
|
|
// 1 cycle earlier, and by marking the functional units as busy
|
|
// for 2 cycles with the result available 1 cycle later than
|
|
// is really the case.
|
|
pipe_class ialu_reg_flags( iRegI op2_out, iRegI op2_in, iRegI op1, flagsReg cr ) %{
|
|
single_instruction;
|
|
op2_out : C(write);
|
|
op1 : R(read);
|
|
cr : R(read); // This is really E, with a 1 cycle stall
|
|
BR : R(2);
|
|
MS : R(2);
|
|
%}
|
|
|
|
pipe_class ialu_clr_and_mover( iRegI dst, iRegP src ) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : C(write)+1;
|
|
src : R(read)+1;
|
|
IALU : R(1);
|
|
BR : E(2);
|
|
MS : E(2);
|
|
%}
|
|
|
|
// Integer ALU reg operation
|
|
pipe_class ialu_move_reg_L_to_I(iRegI dst, iRegL src) %{
|
|
single_instruction; may_have_no_code;
|
|
dst : E(write);
|
|
src : R(read);
|
|
IALU : R;
|
|
%}
|
|
pipe_class ialu_move_reg_I_to_L(iRegL dst, iRegI src) %{
|
|
single_instruction; may_have_no_code;
|
|
dst : E(write);
|
|
src : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Two integer ALU reg operations
|
|
pipe_class ialu_reg_2(iRegL dst, iRegL src) %{
|
|
instruction_count(2);
|
|
dst : E(write);
|
|
src : R(read);
|
|
A0 : R;
|
|
A1 : R;
|
|
%}
|
|
|
|
// Two integer ALU reg operations
|
|
pipe_class ialu_move_reg_L_to_L(iRegL dst, iRegL src) %{
|
|
instruction_count(2); may_have_no_code;
|
|
dst : E(write);
|
|
src : R(read);
|
|
A0 : R;
|
|
A1 : R;
|
|
%}
|
|
|
|
// Integer ALU imm operation
|
|
pipe_class ialu_imm(iRegI dst, immI13 src) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU reg-reg with carry operation
|
|
pipe_class ialu_reg_reg_cy(iRegI dst, iRegI src1, iRegI src2, iRegI cy) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU cc operation
|
|
pipe_class ialu_cc(iRegI dst, flagsReg cc) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
cc : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU cc / second IALU operation
|
|
pipe_class ialu_reg_ialu( iRegI dst, iRegI src ) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : E(write)+1;
|
|
src : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU cc / second IALU operation
|
|
pipe_class ialu_reg_reg_ialu( iRegI dst, iRegI p, iRegI q ) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : E(write)+1;
|
|
p : R(read);
|
|
q : R(read);
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU hi-lo-reg operation
|
|
pipe_class ialu_hi_lo_reg(iRegI dst, immI src) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : E(write)+1;
|
|
IALU : R(2);
|
|
%}
|
|
|
|
// Float ALU hi-lo-reg operation (with temp)
|
|
pipe_class ialu_hi_lo_reg_temp(regF dst, immF src, g3RegP tmp) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : E(write)+1;
|
|
IALU : R(2);
|
|
%}
|
|
|
|
// Long Constant
|
|
pipe_class loadConL( iRegL dst, immL src ) %{
|
|
instruction_count(2); multiple_bundles;
|
|
dst : E(write)+1;
|
|
IALU : R(2);
|
|
IALU : R(2);
|
|
%}
|
|
|
|
// Pointer Constant
|
|
pipe_class loadConP( iRegP dst, immP src ) %{
|
|
instruction_count(0); multiple_bundles;
|
|
fixed_latency(6);
|
|
%}
|
|
|
|
// Polling Address
|
|
pipe_class loadConP_poll( iRegP dst, immP_poll src ) %{
|
|
instruction_count(0); multiple_bundles;
|
|
fixed_latency(6);
|
|
%}
|
|
|
|
// Long Constant small
|
|
pipe_class loadConLlo( iRegL dst, immL src ) %{
|
|
instruction_count(2);
|
|
dst : E(write);
|
|
IALU : R;
|
|
IALU : R;
|
|
%}
|
|
|
|
// [PHH] This is wrong for 64-bit. See LdImmF/D.
|
|
pipe_class loadConFD(regF dst, immF src, g3RegP tmp) %{
|
|
instruction_count(1); multiple_bundles;
|
|
src : R(read);
|
|
dst : M(write)+1;
|
|
IALU : R;
|
|
MS : E;
|
|
%}
|
|
|
|
// Integer ALU nop operation
|
|
pipe_class ialu_nop() %{
|
|
single_instruction;
|
|
IALU : R;
|
|
%}
|
|
|
|
// Integer ALU nop operation
|
|
pipe_class ialu_nop_A0() %{
|
|
single_instruction;
|
|
A0 : R;
|
|
%}
|
|
|
|
// Integer ALU nop operation
|
|
pipe_class ialu_nop_A1() %{
|
|
single_instruction;
|
|
A1 : R;
|
|
%}
|
|
|
|
// Integer Multiply reg-reg operation
|
|
pipe_class imul_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
MS : R(5);
|
|
%}
|
|
|
|
// Integer Multiply reg-imm operation
|
|
pipe_class imul_reg_imm(iRegI dst, iRegI src1, immI13 src2) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
src1 : R(read);
|
|
MS : R(5);
|
|
%}
|
|
|
|
pipe_class mulL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
single_instruction;
|
|
dst : E(write)+4;
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
MS : R(6);
|
|
%}
|
|
|
|
pipe_class mulL_reg_imm(iRegL dst, iRegL src1, immL13 src2) %{
|
|
single_instruction;
|
|
dst : E(write)+4;
|
|
src1 : R(read);
|
|
MS : R(6);
|
|
%}
|
|
|
|
// Integer Divide reg-reg
|
|
pipe_class sdiv_reg_reg(iRegI dst, iRegI src1, iRegI src2, iRegI temp, flagsReg cr) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : E(write);
|
|
temp : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
temp : R(read);
|
|
MS : R(38);
|
|
%}
|
|
|
|
// Integer Divide reg-imm
|
|
pipe_class sdiv_reg_imm(iRegI dst, iRegI src1, immI13 src2, iRegI temp, flagsReg cr) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : E(write);
|
|
temp : E(write);
|
|
src1 : R(read);
|
|
temp : R(read);
|
|
MS : R(38);
|
|
%}
|
|
|
|
// Long Divide
|
|
pipe_class divL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
dst : E(write)+71;
|
|
src1 : R(read);
|
|
src2 : R(read)+1;
|
|
MS : R(70);
|
|
%}
|
|
|
|
pipe_class divL_reg_imm(iRegL dst, iRegL src1, immL13 src2) %{
|
|
dst : E(write)+71;
|
|
src1 : R(read);
|
|
MS : R(70);
|
|
%}
|
|
|
|
// Floating Point Add Float
|
|
pipe_class faddF_reg_reg(regF dst, regF src1, regF src2) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Add Double
|
|
pipe_class faddD_reg_reg(regD dst, regD src1, regD src2) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Conditional Move based on integer flags
|
|
pipe_class int_conditional_float_move (cmpOp cmp, flagsReg cr, regF dst, regF src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
cr : R(read);
|
|
FA : R(2);
|
|
BR : R(2);
|
|
%}
|
|
|
|
// Floating Point Conditional Move based on integer flags
|
|
pipe_class int_conditional_double_move (cmpOp cmp, flagsReg cr, regD dst, regD src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
cr : R(read);
|
|
FA : R(2);
|
|
BR : R(2);
|
|
%}
|
|
|
|
// Floating Point Multiply Float
|
|
pipe_class fmulF_reg_reg(regF dst, regF src1, regF src2) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
FM : R;
|
|
%}
|
|
|
|
// Floating Point Multiply Double
|
|
pipe_class fmulD_reg_reg(regD dst, regD src1, regD src2) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
FM : R;
|
|
%}
|
|
|
|
// Floating Point Divide Float
|
|
pipe_class fdivF_reg_reg(regF dst, regF src1, regF src2) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
FM : R;
|
|
FDIV : C(14);
|
|
%}
|
|
|
|
// Floating Point Divide Double
|
|
pipe_class fdivD_reg_reg(regD dst, regD src1, regD src2) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
FM : R;
|
|
FDIV : C(17);
|
|
%}
|
|
|
|
// Fused floating-point multiply-add float.
|
|
pipe_class fmaF_regx4(regF dst, regF src1, regF src2, regF src3) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
src3 : E(read);
|
|
FM : R;
|
|
%}
|
|
|
|
// Fused gloating-point multiply-add double.
|
|
pipe_class fmaD_regx4(regD dst, regD src1, regD src2, regD src3) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
src3 : E(read);
|
|
FM : R;
|
|
%}
|
|
|
|
// Floating Point Move/Negate/Abs Float
|
|
pipe_class faddF_reg(regF dst, regF src) %{
|
|
single_instruction;
|
|
dst : W(write);
|
|
src : E(read);
|
|
FA : R(1);
|
|
%}
|
|
|
|
// Floating Point Move/Negate/Abs Double
|
|
pipe_class faddD_reg(regD dst, regD src) %{
|
|
single_instruction;
|
|
dst : W(write);
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert F->D
|
|
pipe_class fcvtF2D(regD dst, regF src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert I->D
|
|
pipe_class fcvtI2D(regD dst, regF src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert LHi->D
|
|
pipe_class fcvtLHi2D(regD dst, regD src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert L->D
|
|
pipe_class fcvtL2D(regD dst, regF src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert L->F
|
|
pipe_class fcvtL2F(regD dst, regF src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert D->F
|
|
pipe_class fcvtD2F(regD dst, regF src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert I->L
|
|
pipe_class fcvtI2L(regD dst, regF src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert D->F
|
|
pipe_class fcvtD2I(regF dst, regD src, flagsReg cr) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : X(write)+6;
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert D->L
|
|
pipe_class fcvtD2L(regD dst, regD src, flagsReg cr) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : X(write)+6;
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert F->I
|
|
pipe_class fcvtF2I(regF dst, regF src, flagsReg cr) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : X(write)+6;
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert F->L
|
|
pipe_class fcvtF2L(regD dst, regF src, flagsReg cr) %{
|
|
instruction_count(1); multiple_bundles;
|
|
dst : X(write)+6;
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Convert I->F
|
|
pipe_class fcvtI2F(regF dst, regF src) %{
|
|
single_instruction;
|
|
dst : X(write);
|
|
src : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Compare
|
|
pipe_class faddF_fcc_reg_reg_zero(flagsRegF cr, regF src1, regF src2, immI0 zero) %{
|
|
single_instruction;
|
|
cr : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Point Compare
|
|
pipe_class faddD_fcc_reg_reg_zero(flagsRegF cr, regD src1, regD src2, immI0 zero) %{
|
|
single_instruction;
|
|
cr : X(write);
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
FA : R;
|
|
%}
|
|
|
|
// Floating Add Nop
|
|
pipe_class fadd_nop() %{
|
|
single_instruction;
|
|
FA : R;
|
|
%}
|
|
|
|
// Integer Store to Memory
|
|
pipe_class istore_mem_reg(memory mem, iRegI src) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
src : C(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Integer Store to Memory
|
|
pipe_class istore_mem_spORreg(memory mem, sp_ptr_RegP src) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
src : C(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Integer Store Zero to Memory
|
|
pipe_class istore_mem_zero(memory mem, immI0 src) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Special Stack Slot Store
|
|
pipe_class istore_stk_reg(stackSlotI stkSlot, iRegI src) %{
|
|
single_instruction;
|
|
stkSlot : R(read);
|
|
src : C(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Special Stack Slot Store
|
|
pipe_class lstoreI_stk_reg(stackSlotL stkSlot, iRegI src) %{
|
|
instruction_count(2); multiple_bundles;
|
|
stkSlot : R(read);
|
|
src : C(read);
|
|
MS : R(2);
|
|
%}
|
|
|
|
// Float Store
|
|
pipe_class fstoreF_mem_reg(memory mem, RegF src) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
src : C(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Float Store
|
|
pipe_class fstoreF_mem_zero(memory mem, immF0 src) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Double Store
|
|
pipe_class fstoreD_mem_reg(memory mem, RegD src) %{
|
|
instruction_count(1);
|
|
mem : R(read);
|
|
src : C(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Double Store
|
|
pipe_class fstoreD_mem_zero(memory mem, immD0 src) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Special Stack Slot Float Store
|
|
pipe_class fstoreF_stk_reg(stackSlotI stkSlot, RegF src) %{
|
|
single_instruction;
|
|
stkSlot : R(read);
|
|
src : C(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Special Stack Slot Double Store
|
|
pipe_class fstoreD_stk_reg(stackSlotI stkSlot, RegD src) %{
|
|
single_instruction;
|
|
stkSlot : R(read);
|
|
src : C(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// Integer Load (when sign bit propagation not needed)
|
|
pipe_class iload_mem(iRegI dst, memory mem) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
dst : C(write);
|
|
MS : R;
|
|
%}
|
|
|
|
// Integer Load from stack operand
|
|
pipe_class iload_stkD(iRegI dst, stackSlotD mem ) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
dst : C(write);
|
|
MS : R;
|
|
%}
|
|
|
|
// Integer Load (when sign bit propagation or masking is needed)
|
|
pipe_class iload_mask_mem(iRegI dst, memory mem) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
dst : M(write);
|
|
MS : R;
|
|
%}
|
|
|
|
// Float Load
|
|
pipe_class floadF_mem(regF dst, memory mem) %{
|
|
single_instruction;
|
|
mem : R(read);
|
|
dst : M(write);
|
|
MS : R;
|
|
%}
|
|
|
|
// Float Load
|
|
pipe_class floadD_mem(regD dst, memory mem) %{
|
|
instruction_count(1); multiple_bundles; // Again, unaligned argument is only multiple case
|
|
mem : R(read);
|
|
dst : M(write);
|
|
MS : R;
|
|
%}
|
|
|
|
// Float Load
|
|
pipe_class floadF_stk(regF dst, stackSlotI stkSlot) %{
|
|
single_instruction;
|
|
stkSlot : R(read);
|
|
dst : M(write);
|
|
MS : R;
|
|
%}
|
|
|
|
// Float Load
|
|
pipe_class floadD_stk(regD dst, stackSlotI stkSlot) %{
|
|
single_instruction;
|
|
stkSlot : R(read);
|
|
dst : M(write);
|
|
MS : R;
|
|
%}
|
|
|
|
// Memory Nop
|
|
pipe_class mem_nop() %{
|
|
single_instruction;
|
|
MS : R;
|
|
%}
|
|
|
|
pipe_class sethi(iRegP dst, immI src) %{
|
|
single_instruction;
|
|
dst : E(write);
|
|
IALU : R;
|
|
%}
|
|
|
|
pipe_class loadPollP(iRegP poll) %{
|
|
single_instruction;
|
|
poll : R(read);
|
|
MS : R;
|
|
%}
|
|
|
|
pipe_class br(Universe br, label labl) %{
|
|
single_instruction_with_delay_slot;
|
|
BR : R;
|
|
%}
|
|
|
|
pipe_class br_cc(Universe br, cmpOp cmp, flagsReg cr, label labl) %{
|
|
single_instruction_with_delay_slot;
|
|
cr : E(read);
|
|
BR : R;
|
|
%}
|
|
|
|
pipe_class br_reg(Universe br, cmpOp cmp, iRegI op1, label labl) %{
|
|
single_instruction_with_delay_slot;
|
|
op1 : E(read);
|
|
BR : R;
|
|
MS : R;
|
|
%}
|
|
|
|
// Compare and branch
|
|
pipe_class cmp_br_reg_reg(Universe br, cmpOp cmp, iRegI src1, iRegI src2, label labl, flagsReg cr) %{
|
|
instruction_count(2); has_delay_slot;
|
|
cr : E(write);
|
|
src1 : R(read);
|
|
src2 : R(read);
|
|
IALU : R;
|
|
BR : R;
|
|
%}
|
|
|
|
// Compare and branch
|
|
pipe_class cmp_br_reg_imm(Universe br, cmpOp cmp, iRegI src1, immI13 src2, label labl, flagsReg cr) %{
|
|
instruction_count(2); has_delay_slot;
|
|
cr : E(write);
|
|
src1 : R(read);
|
|
IALU : R;
|
|
BR : R;
|
|
%}
|
|
|
|
// Compare and branch using cbcond
|
|
pipe_class cbcond_reg_reg(Universe br, cmpOp cmp, iRegI src1, iRegI src2, label labl) %{
|
|
single_instruction;
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
IALU : R;
|
|
BR : R;
|
|
%}
|
|
|
|
// Compare and branch using cbcond
|
|
pipe_class cbcond_reg_imm(Universe br, cmpOp cmp, iRegI src1, immI5 src2, label labl) %{
|
|
single_instruction;
|
|
src1 : E(read);
|
|
IALU : R;
|
|
BR : R;
|
|
%}
|
|
|
|
pipe_class br_fcc(Universe br, cmpOpF cc, flagsReg cr, label labl) %{
|
|
single_instruction_with_delay_slot;
|
|
cr : E(read);
|
|
BR : R;
|
|
%}
|
|
|
|
pipe_class br_nop() %{
|
|
single_instruction;
|
|
BR : R;
|
|
%}
|
|
|
|
pipe_class simple_call(method meth) %{
|
|
instruction_count(2); multiple_bundles; force_serialization;
|
|
fixed_latency(100);
|
|
BR : R(1);
|
|
MS : R(1);
|
|
A0 : R(1);
|
|
%}
|
|
|
|
pipe_class compiled_call(method meth) %{
|
|
instruction_count(1); multiple_bundles; force_serialization;
|
|
fixed_latency(100);
|
|
MS : R(1);
|
|
%}
|
|
|
|
pipe_class call(method meth) %{
|
|
instruction_count(0); multiple_bundles; force_serialization;
|
|
fixed_latency(100);
|
|
%}
|
|
|
|
pipe_class tail_call(Universe ignore, label labl) %{
|
|
single_instruction; has_delay_slot;
|
|
fixed_latency(100);
|
|
BR : R(1);
|
|
MS : R(1);
|
|
%}
|
|
|
|
pipe_class ret(Universe ignore) %{
|
|
single_instruction; has_delay_slot;
|
|
BR : R(1);
|
|
MS : R(1);
|
|
%}
|
|
|
|
pipe_class ret_poll(g3RegP poll) %{
|
|
instruction_count(3); has_delay_slot;
|
|
poll : E(read);
|
|
MS : R;
|
|
%}
|
|
|
|
// The real do-nothing guy
|
|
pipe_class empty( ) %{
|
|
instruction_count(0);
|
|
%}
|
|
|
|
pipe_class long_memory_op() %{
|
|
instruction_count(0); multiple_bundles; force_serialization;
|
|
fixed_latency(25);
|
|
MS : R(1);
|
|
%}
|
|
|
|
// Check-cast
|
|
pipe_class partial_subtype_check_pipe(Universe ignore, iRegP array, iRegP match ) %{
|
|
array : R(read);
|
|
match : R(read);
|
|
IALU : R(2);
|
|
BR : R(2);
|
|
MS : R;
|
|
%}
|
|
|
|
// Convert FPU flags into +1,0,-1
|
|
pipe_class floating_cmp( iRegI dst, regF src1, regF src2 ) %{
|
|
src1 : E(read);
|
|
src2 : E(read);
|
|
dst : E(write);
|
|
FA : R;
|
|
MS : R(2);
|
|
BR : R(2);
|
|
%}
|
|
|
|
// Compare for p < q, and conditionally add y
|
|
pipe_class cadd_cmpltmask( iRegI p, iRegI q, iRegI y ) %{
|
|
p : E(read);
|
|
q : E(read);
|
|
y : E(read);
|
|
IALU : R(3)
|
|
%}
|
|
|
|
// Perform a compare, then move conditionally in a branch delay slot.
|
|
pipe_class min_max( iRegI src2, iRegI srcdst ) %{
|
|
src2 : E(read);
|
|
srcdst : E(read);
|
|
IALU : R;
|
|
BR : R;
|
|
%}
|
|
|
|
// Define the class for the Nop node
|
|
define %{
|
|
MachNop = ialu_nop;
|
|
%}
|
|
|
|
%}
|
|
|
|
//----------INSTRUCTIONS-------------------------------------------------------
|
|
|
|
//------------Special Stack Slot instructions - no match rules-----------------
|
|
instruct stkI_to_regF(regF dst, stackSlotI src) %{
|
|
// No match rule to avoid chain rule match.
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "LDF $src,$dst\t! stkI to regF" %}
|
|
opcode(Assembler::ldf_op3);
|
|
ins_encode(simple_form3_mem_reg(src, dst));
|
|
ins_pipe(floadF_stk);
|
|
%}
|
|
|
|
instruct stkL_to_regD(regD dst, stackSlotL src) %{
|
|
// No match rule to avoid chain rule match.
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "LDDF $src,$dst\t! stkL to regD" %}
|
|
opcode(Assembler::lddf_op3);
|
|
ins_encode(simple_form3_mem_reg(src, dst));
|
|
ins_pipe(floadD_stk);
|
|
%}
|
|
|
|
instruct regF_to_stkI(stackSlotI dst, regF src) %{
|
|
// No match rule to avoid chain rule match.
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "STF $src,$dst\t! regF to stkI" %}
|
|
opcode(Assembler::stf_op3);
|
|
ins_encode(simple_form3_mem_reg(dst, src));
|
|
ins_pipe(fstoreF_stk_reg);
|
|
%}
|
|
|
|
instruct regD_to_stkL(stackSlotL dst, regD src) %{
|
|
// No match rule to avoid chain rule match.
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "STDF $src,$dst\t! regD to stkL" %}
|
|
opcode(Assembler::stdf_op3);
|
|
ins_encode(simple_form3_mem_reg(dst, src));
|
|
ins_pipe(fstoreD_stk_reg);
|
|
%}
|
|
|
|
instruct regI_to_stkLHi(stackSlotL dst, iRegI src) %{
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST*2);
|
|
format %{ "STW $src,$dst.hi\t! long\n\t"
|
|
"STW R_G0,$dst.lo" %}
|
|
opcode(Assembler::stw_op3);
|
|
ins_encode(simple_form3_mem_reg(dst, src), form3_mem_plus_4_reg(dst, R_G0));
|
|
ins_pipe(lstoreI_stk_reg);
|
|
%}
|
|
|
|
instruct regL_to_stkD(stackSlotD dst, iRegL src) %{
|
|
// No match rule to avoid chain rule match.
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "STX $src,$dst\t! regL to stkD" %}
|
|
opcode(Assembler::stx_op3);
|
|
ins_encode(simple_form3_mem_reg( dst, src ) );
|
|
ins_pipe(istore_stk_reg);
|
|
%}
|
|
|
|
//---------- Chain stack slots between similar types --------
|
|
|
|
// Load integer from stack slot
|
|
instruct stkI_to_regI( iRegI dst, stackSlotI src ) %{
|
|
match(Set dst src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDUW $src,$dst\t!stk" %}
|
|
opcode(Assembler::lduw_op3);
|
|
ins_encode(simple_form3_mem_reg( src, dst ) );
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Store integer to stack slot
|
|
instruct regI_to_stkI( stackSlotI dst, iRegI src ) %{
|
|
match(Set dst src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STW $src,$dst\t!stk" %}
|
|
opcode(Assembler::stw_op3);
|
|
ins_encode(simple_form3_mem_reg( dst, src ) );
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
// Load long from stack slot
|
|
instruct stkL_to_regL( iRegL dst, stackSlotL src ) %{
|
|
match(Set dst src);
|
|
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "LDX $src,$dst\t! long" %}
|
|
opcode(Assembler::ldx_op3);
|
|
ins_encode(simple_form3_mem_reg( src, dst ) );
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Store long to stack slot
|
|
instruct regL_to_stkL(stackSlotL dst, iRegL src) %{
|
|
match(Set dst src);
|
|
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "STX $src,$dst\t! long" %}
|
|
opcode(Assembler::stx_op3);
|
|
ins_encode(simple_form3_mem_reg( dst, src ) );
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
// Load pointer from stack slot, 64-bit encoding
|
|
instruct stkP_to_regP( iRegP dst, stackSlotP src ) %{
|
|
match(Set dst src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "LDX $src,$dst\t!ptr" %}
|
|
opcode(Assembler::ldx_op3);
|
|
ins_encode(simple_form3_mem_reg( src, dst ) );
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Store pointer to stack slot
|
|
instruct regP_to_stkP(stackSlotP dst, iRegP src) %{
|
|
match(Set dst src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "STX $src,$dst\t!ptr" %}
|
|
opcode(Assembler::stx_op3);
|
|
ins_encode(simple_form3_mem_reg( dst, src ) );
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
//------------Special Nop instructions for bundling - no match rules-----------
|
|
// Nop using the A0 functional unit
|
|
instruct Nop_A0() %{
|
|
ins_cost(0);
|
|
|
|
format %{ "NOP ! Alu Pipeline" %}
|
|
opcode(Assembler::or_op3, Assembler::arith_op);
|
|
ins_encode( form2_nop() );
|
|
ins_pipe(ialu_nop_A0);
|
|
%}
|
|
|
|
// Nop using the A1 functional unit
|
|
instruct Nop_A1( ) %{
|
|
ins_cost(0);
|
|
|
|
format %{ "NOP ! Alu Pipeline" %}
|
|
opcode(Assembler::or_op3, Assembler::arith_op);
|
|
ins_encode( form2_nop() );
|
|
ins_pipe(ialu_nop_A1);
|
|
%}
|
|
|
|
// Nop using the memory functional unit
|
|
instruct Nop_MS( ) %{
|
|
ins_cost(0);
|
|
|
|
format %{ "NOP ! Memory Pipeline" %}
|
|
ins_encode( emit_mem_nop );
|
|
ins_pipe(mem_nop);
|
|
%}
|
|
|
|
// Nop using the floating add functional unit
|
|
instruct Nop_FA( ) %{
|
|
ins_cost(0);
|
|
|
|
format %{ "NOP ! Floating Add Pipeline" %}
|
|
ins_encode( emit_fadd_nop );
|
|
ins_pipe(fadd_nop);
|
|
%}
|
|
|
|
// Nop using the branch functional unit
|
|
instruct Nop_BR( ) %{
|
|
ins_cost(0);
|
|
|
|
format %{ "NOP ! Branch Pipeline" %}
|
|
ins_encode( emit_br_nop );
|
|
ins_pipe(br_nop);
|
|
%}
|
|
|
|
//----------Load/Store/Move Instructions---------------------------------------
|
|
//----------Load Instructions--------------------------------------------------
|
|
// Load Byte (8bit signed)
|
|
instruct loadB(iRegI dst, memory mem) %{
|
|
match(Set dst (LoadB mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDSB $mem,$dst\t! byte" %}
|
|
ins_encode %{
|
|
__ ldsb($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Byte (8bit signed) into a Long Register
|
|
instruct loadB2L(iRegL dst, memory mem) %{
|
|
match(Set dst (ConvI2L (LoadB mem)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDSB $mem,$dst\t! byte -> long" %}
|
|
ins_encode %{
|
|
__ ldsb($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Unsigned Byte (8bit UNsigned) into an int reg
|
|
instruct loadUB(iRegI dst, memory mem) %{
|
|
match(Set dst (LoadUB mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDUB $mem,$dst\t! ubyte" %}
|
|
ins_encode %{
|
|
__ ldub($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Unsigned Byte (8bit UNsigned) into a Long Register
|
|
instruct loadUB2L(iRegL dst, memory mem) %{
|
|
match(Set dst (ConvI2L (LoadUB mem)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDUB $mem,$dst\t! ubyte -> long" %}
|
|
ins_encode %{
|
|
__ ldub($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Unsigned Byte (8 bit UNsigned) with 32-bit mask into Long Register
|
|
instruct loadUB2L_immI(iRegL dst, memory mem, immI mask) %{
|
|
match(Set dst (ConvI2L (AndI (LoadUB mem) mask)));
|
|
ins_cost(MEMORY_REF_COST + DEFAULT_COST);
|
|
|
|
size(2*4);
|
|
format %{ "LDUB $mem,$dst\t# ubyte & 32-bit mask -> long\n\t"
|
|
"AND $dst,right_n_bits($mask, 8),$dst" %}
|
|
ins_encode %{
|
|
__ ldub($mem$$Address, $dst$$Register);
|
|
__ and3($dst$$Register, $mask$$constant & right_n_bits(8), $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Short (16bit signed)
|
|
instruct loadS(iRegI dst, memory mem) %{
|
|
match(Set dst (LoadS mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDSH $mem,$dst\t! short" %}
|
|
ins_encode %{
|
|
__ ldsh($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Short (16 bit signed) to Byte (8 bit signed)
|
|
instruct loadS2B(iRegI dst, indOffset13m7 mem, immI_24 twentyfour) %{
|
|
match(Set dst (RShiftI (LShiftI (LoadS mem) twentyfour) twentyfour));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
|
|
format %{ "LDSB $mem+1,$dst\t! short -> byte" %}
|
|
ins_encode %{
|
|
__ ldsb($mem$$Address, $dst$$Register, 1);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Short (16bit signed) into a Long Register
|
|
instruct loadS2L(iRegL dst, memory mem) %{
|
|
match(Set dst (ConvI2L (LoadS mem)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDSH $mem,$dst\t! short -> long" %}
|
|
ins_encode %{
|
|
__ ldsh($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Unsigned Short/Char (16bit UNsigned)
|
|
instruct loadUS(iRegI dst, memory mem) %{
|
|
match(Set dst (LoadUS mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDUH $mem,$dst\t! ushort/char" %}
|
|
ins_encode %{
|
|
__ lduh($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Unsigned Short/Char (16 bit UNsigned) to Byte (8 bit signed)
|
|
instruct loadUS2B(iRegI dst, indOffset13m7 mem, immI_24 twentyfour) %{
|
|
match(Set dst (RShiftI (LShiftI (LoadUS mem) twentyfour) twentyfour));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDSB $mem+1,$dst\t! ushort -> byte" %}
|
|
ins_encode %{
|
|
__ ldsb($mem$$Address, $dst$$Register, 1);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Unsigned Short/Char (16bit UNsigned) into a Long Register
|
|
instruct loadUS2L(iRegL dst, memory mem) %{
|
|
match(Set dst (ConvI2L (LoadUS mem)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDUH $mem,$dst\t! ushort/char -> long" %}
|
|
ins_encode %{
|
|
__ lduh($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Unsigned Short/Char (16bit UNsigned) with mask 0xFF into a Long Register
|
|
instruct loadUS2L_immI_255(iRegL dst, indOffset13m7 mem, immI_255 mask) %{
|
|
match(Set dst (ConvI2L (AndI (LoadUS mem) mask)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDUB $mem+1,$dst\t! ushort/char & 0xFF -> long" %}
|
|
ins_encode %{
|
|
__ ldub($mem$$Address, $dst$$Register, 1); // LSB is index+1 on BE
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Unsigned Short/Char (16bit UNsigned) with a 13-bit mask into a Long Register
|
|
instruct loadUS2L_immI13(iRegL dst, memory mem, immI13 mask) %{
|
|
match(Set dst (ConvI2L (AndI (LoadUS mem) mask)));
|
|
ins_cost(MEMORY_REF_COST + DEFAULT_COST);
|
|
|
|
size(2*4);
|
|
format %{ "LDUH $mem,$dst\t! ushort/char & 13-bit mask -> long\n\t"
|
|
"AND $dst,$mask,$dst" %}
|
|
ins_encode %{
|
|
Register Rdst = $dst$$Register;
|
|
__ lduh($mem$$Address, Rdst);
|
|
__ and3(Rdst, $mask$$constant, Rdst);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Unsigned Short/Char (16bit UNsigned) with a 32-bit mask into a Long Register
|
|
instruct loadUS2L_immI(iRegL dst, memory mem, immI mask, iRegL tmp) %{
|
|
match(Set dst (ConvI2L (AndI (LoadUS mem) mask)));
|
|
effect(TEMP dst, TEMP tmp);
|
|
ins_cost(MEMORY_REF_COST + 2*DEFAULT_COST);
|
|
|
|
format %{ "LDUH $mem,$dst\t! ushort/char & 32-bit mask -> long\n\t"
|
|
"SET right_n_bits($mask, 16),$tmp\n\t"
|
|
"AND $dst,$tmp,$dst" %}
|
|
ins_encode %{
|
|
Register Rdst = $dst$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
__ lduh($mem$$Address, Rdst);
|
|
__ set($mask$$constant & right_n_bits(16), Rtmp);
|
|
__ and3(Rdst, Rtmp, Rdst);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Integer
|
|
instruct loadI(iRegI dst, memory mem) %{
|
|
match(Set dst (LoadI mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDUW $mem,$dst\t! int" %}
|
|
ins_encode %{
|
|
__ lduw($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Integer to Byte (8 bit signed)
|
|
instruct loadI2B(iRegI dst, indOffset13m7 mem, immI_24 twentyfour) %{
|
|
match(Set dst (RShiftI (LShiftI (LoadI mem) twentyfour) twentyfour));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
|
|
format %{ "LDSB $mem+3,$dst\t! int -> byte" %}
|
|
ins_encode %{
|
|
__ ldsb($mem$$Address, $dst$$Register, 3);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Integer to Unsigned Byte (8 bit UNsigned)
|
|
instruct loadI2UB(iRegI dst, indOffset13m7 mem, immI_255 mask) %{
|
|
match(Set dst (AndI (LoadI mem) mask));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
|
|
format %{ "LDUB $mem+3,$dst\t! int -> ubyte" %}
|
|
ins_encode %{
|
|
__ ldub($mem$$Address, $dst$$Register, 3);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Integer to Short (16 bit signed)
|
|
instruct loadI2S(iRegI dst, indOffset13m7 mem, immI_16 sixteen) %{
|
|
match(Set dst (RShiftI (LShiftI (LoadI mem) sixteen) sixteen));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
|
|
format %{ "LDSH $mem+2,$dst\t! int -> short" %}
|
|
ins_encode %{
|
|
__ ldsh($mem$$Address, $dst$$Register, 2);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Integer to Unsigned Short (16 bit UNsigned)
|
|
instruct loadI2US(iRegI dst, indOffset13m7 mem, immI_65535 mask) %{
|
|
match(Set dst (AndI (LoadI mem) mask));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
|
|
format %{ "LDUH $mem+2,$dst\t! int -> ushort/char" %}
|
|
ins_encode %{
|
|
__ lduh($mem$$Address, $dst$$Register, 2);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Integer into a Long Register
|
|
instruct loadI2L(iRegL dst, memory mem) %{
|
|
match(Set dst (ConvI2L (LoadI mem)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDSW $mem,$dst\t! int -> long" %}
|
|
ins_encode %{
|
|
__ ldsw($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mask_mem);
|
|
%}
|
|
|
|
// Load Integer with mask 0xFF into a Long Register
|
|
instruct loadI2L_immI_255(iRegL dst, indOffset13m7 mem, immI_255 mask) %{
|
|
match(Set dst (ConvI2L (AndI (LoadI mem) mask)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDUB $mem+3,$dst\t! int & 0xFF -> long" %}
|
|
ins_encode %{
|
|
__ ldub($mem$$Address, $dst$$Register, 3); // LSB is index+3 on BE
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Integer with mask 0xFFFF into a Long Register
|
|
instruct loadI2L_immI_65535(iRegL dst, indOffset13m7 mem, immI_65535 mask) %{
|
|
match(Set dst (ConvI2L (AndI (LoadI mem) mask)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDUH $mem+2,$dst\t! int & 0xFFFF -> long" %}
|
|
ins_encode %{
|
|
__ lduh($mem$$Address, $dst$$Register, 2); // LSW is index+2 on BE
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Integer with a 12-bit mask into a Long Register
|
|
instruct loadI2L_immU12(iRegL dst, memory mem, immU12 mask) %{
|
|
match(Set dst (ConvI2L (AndI (LoadI mem) mask)));
|
|
ins_cost(MEMORY_REF_COST + DEFAULT_COST);
|
|
|
|
size(2*4);
|
|
format %{ "LDUW $mem,$dst\t! int & 12-bit mask -> long\n\t"
|
|
"AND $dst,$mask,$dst" %}
|
|
ins_encode %{
|
|
Register Rdst = $dst$$Register;
|
|
__ lduw($mem$$Address, Rdst);
|
|
__ and3(Rdst, $mask$$constant, Rdst);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Integer with a 31-bit mask into a Long Register
|
|
instruct loadI2L_immU31(iRegL dst, memory mem, immU31 mask, iRegL tmp) %{
|
|
match(Set dst (ConvI2L (AndI (LoadI mem) mask)));
|
|
effect(TEMP dst, TEMP tmp);
|
|
ins_cost(MEMORY_REF_COST + 2*DEFAULT_COST);
|
|
|
|
format %{ "LDUW $mem,$dst\t! int & 31-bit mask -> long\n\t"
|
|
"SET $mask,$tmp\n\t"
|
|
"AND $dst,$tmp,$dst" %}
|
|
ins_encode %{
|
|
Register Rdst = $dst$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
__ lduw($mem$$Address, Rdst);
|
|
__ set($mask$$constant, Rtmp);
|
|
__ and3(Rdst, Rtmp, Rdst);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Unsigned Integer into a Long Register
|
|
instruct loadUI2L(iRegL dst, memory mem, immL_32bits mask) %{
|
|
match(Set dst (AndL (ConvI2L (LoadI mem)) mask));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDUW $mem,$dst\t! uint -> long" %}
|
|
ins_encode %{
|
|
__ lduw($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Long - aligned
|
|
instruct loadL(iRegL dst, memory mem ) %{
|
|
match(Set dst (LoadL mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "LDX $mem,$dst\t! long" %}
|
|
ins_encode %{
|
|
__ ldx($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Long - UNaligned
|
|
instruct loadL_unaligned(iRegL dst, memory mem, o7RegI tmp) %{
|
|
match(Set dst (LoadL_unaligned mem));
|
|
effect(KILL tmp);
|
|
ins_cost(MEMORY_REF_COST*2+DEFAULT_COST);
|
|
format %{ "LDUW $mem+4,R_O7\t! misaligned long\n"
|
|
"\tLDUW $mem ,$dst\n"
|
|
"\tSLLX #32, $dst, $dst\n"
|
|
"\tOR $dst, R_O7, $dst" %}
|
|
opcode(Assembler::lduw_op3);
|
|
ins_encode(form3_mem_reg_long_unaligned_marshal( mem, dst ));
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Range
|
|
instruct loadRange(iRegI dst, memory mem) %{
|
|
match(Set dst (LoadRange mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDUW $mem,$dst\t! range" %}
|
|
opcode(Assembler::lduw_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, dst ) );
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Integer into %f register (for fitos/fitod)
|
|
instruct loadI_freg(regF dst, memory mem) %{
|
|
match(Set dst (LoadI mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDF $mem,$dst\t! for fitos/fitod" %}
|
|
opcode(Assembler::ldf_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, dst ) );
|
|
ins_pipe(floadF_mem);
|
|
%}
|
|
|
|
// Load Pointer
|
|
instruct loadP(iRegP dst, memory mem) %{
|
|
match(Set dst (LoadP mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
|
|
format %{ "LDX $mem,$dst\t! ptr" %}
|
|
ins_encode %{
|
|
__ ldx($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Compressed Pointer
|
|
instruct loadN(iRegN dst, memory mem) %{
|
|
match(Set dst (LoadN mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
|
|
format %{ "LDUW $mem,$dst\t! compressed ptr" %}
|
|
ins_encode %{
|
|
__ lduw($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Klass Pointer
|
|
instruct loadKlass(iRegP dst, memory mem) %{
|
|
match(Set dst (LoadKlass mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
|
|
format %{ "LDX $mem,$dst\t! klass ptr" %}
|
|
ins_encode %{
|
|
__ ldx($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load narrow Klass Pointer
|
|
instruct loadNKlass(iRegN dst, memory mem) %{
|
|
match(Set dst (LoadNKlass mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
|
|
format %{ "LDUW $mem,$dst\t! compressed klass ptr" %}
|
|
ins_encode %{
|
|
__ lduw($mem$$Address, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Double
|
|
instruct loadD(regD dst, memory mem) %{
|
|
match(Set dst (LoadD mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDDF $mem,$dst" %}
|
|
opcode(Assembler::lddf_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, dst ) );
|
|
ins_pipe(floadD_mem);
|
|
%}
|
|
|
|
// Load Double - UNaligned
|
|
instruct loadD_unaligned(regD_low dst, memory mem ) %{
|
|
match(Set dst (LoadD_unaligned mem));
|
|
ins_cost(MEMORY_REF_COST*2+DEFAULT_COST);
|
|
format %{ "LDF $mem ,$dst.hi\t! misaligned double\n"
|
|
"\tLDF $mem+4,$dst.lo\t!" %}
|
|
opcode(Assembler::ldf_op3);
|
|
ins_encode( form3_mem_reg_double_unaligned( mem, dst ));
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Float
|
|
instruct loadF(regF dst, memory mem) %{
|
|
match(Set dst (LoadF mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDF $mem,$dst" %}
|
|
opcode(Assembler::ldf_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, dst ) );
|
|
ins_pipe(floadF_mem);
|
|
%}
|
|
|
|
// Load Constant
|
|
instruct loadConI( iRegI dst, immI src ) %{
|
|
match(Set dst src);
|
|
ins_cost(DEFAULT_COST * 3/2);
|
|
format %{ "SET $src,$dst" %}
|
|
ins_encode( Set32(src, dst) );
|
|
ins_pipe(ialu_hi_lo_reg);
|
|
%}
|
|
|
|
instruct loadConI13( iRegI dst, immI13 src ) %{
|
|
match(Set dst src);
|
|
|
|
size(4);
|
|
format %{ "MOV $src,$dst" %}
|
|
ins_encode( Set13( src, dst ) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct loadConP_set(iRegP dst, immP_set con) %{
|
|
match(Set dst con);
|
|
ins_cost(DEFAULT_COST * 3/2);
|
|
format %{ "SET $con,$dst\t! ptr" %}
|
|
ins_encode %{
|
|
relocInfo::relocType constant_reloc = _opnds[1]->constant_reloc();
|
|
intptr_t val = $con$$constant;
|
|
if (constant_reloc == relocInfo::oop_type) {
|
|
__ set_oop_constant((jobject) val, $dst$$Register);
|
|
} else if (constant_reloc == relocInfo::metadata_type) {
|
|
__ set_metadata_constant((Metadata*)val, $dst$$Register);
|
|
} else { // non-oop pointers, e.g. card mark base, heap top
|
|
assert(constant_reloc == relocInfo::none, "unexpected reloc type");
|
|
__ set(val, $dst$$Register);
|
|
}
|
|
%}
|
|
ins_pipe(loadConP);
|
|
%}
|
|
|
|
instruct loadConP_load(iRegP dst, immP_load con) %{
|
|
match(Set dst con);
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "LD [$constanttablebase + $constantoffset],$dst\t! load from constant table: ptr=$con" %}
|
|
ins_encode %{
|
|
RegisterOrConstant con_offset = __ ensure_simm13_or_reg($constantoffset($con), $dst$$Register);
|
|
__ ld_ptr($constanttablebase, con_offset, $dst$$Register);
|
|
%}
|
|
ins_pipe(loadConP);
|
|
%}
|
|
|
|
instruct loadConP_no_oop_cheap(iRegP dst, immP_no_oop_cheap con) %{
|
|
match(Set dst con);
|
|
ins_cost(DEFAULT_COST * 3/2);
|
|
format %{ "SET $con,$dst\t! non-oop ptr" %}
|
|
ins_encode %{
|
|
if (_opnds[1]->constant_reloc() == relocInfo::metadata_type) {
|
|
__ set_metadata_constant((Metadata*)$con$$constant, $dst$$Register);
|
|
} else {
|
|
__ set($con$$constant, $dst$$Register);
|
|
}
|
|
%}
|
|
ins_pipe(loadConP);
|
|
%}
|
|
|
|
instruct loadConP0(iRegP dst, immP0 src) %{
|
|
match(Set dst src);
|
|
|
|
size(4);
|
|
format %{ "CLR $dst\t!ptr" %}
|
|
ins_encode %{
|
|
__ clr($dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct loadConP_poll(iRegP dst, immP_poll src) %{
|
|
match(Set dst src);
|
|
ins_cost(DEFAULT_COST);
|
|
format %{ "SET $src,$dst\t!ptr" %}
|
|
ins_encode %{
|
|
AddressLiteral polling_page(os::get_polling_page());
|
|
__ sethi(polling_page, reg_to_register_object($dst$$reg));
|
|
%}
|
|
ins_pipe(loadConP_poll);
|
|
%}
|
|
|
|
instruct loadConN0(iRegN dst, immN0 src) %{
|
|
match(Set dst src);
|
|
|
|
size(4);
|
|
format %{ "CLR $dst\t! compressed NULL ptr" %}
|
|
ins_encode %{
|
|
__ clr($dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct loadConN(iRegN dst, immN src) %{
|
|
match(Set dst src);
|
|
ins_cost(DEFAULT_COST * 3/2);
|
|
format %{ "SET $src,$dst\t! compressed ptr" %}
|
|
ins_encode %{
|
|
Register dst = $dst$$Register;
|
|
__ set_narrow_oop((jobject)$src$$constant, dst);
|
|
%}
|
|
ins_pipe(ialu_hi_lo_reg);
|
|
%}
|
|
|
|
instruct loadConNKlass(iRegN dst, immNKlass src) %{
|
|
match(Set dst src);
|
|
ins_cost(DEFAULT_COST * 3/2);
|
|
format %{ "SET $src,$dst\t! compressed klass ptr" %}
|
|
ins_encode %{
|
|
Register dst = $dst$$Register;
|
|
__ set_narrow_klass((Klass*)$src$$constant, dst);
|
|
%}
|
|
ins_pipe(ialu_hi_lo_reg);
|
|
%}
|
|
|
|
// Materialize long value (predicated by immL_cheap).
|
|
instruct loadConL_set64(iRegL dst, immL_cheap con, o7RegL tmp) %{
|
|
match(Set dst con);
|
|
effect(KILL tmp);
|
|
ins_cost(DEFAULT_COST * 3);
|
|
format %{ "SET64 $con,$dst KILL $tmp\t! cheap long" %}
|
|
ins_encode %{
|
|
__ set64($con$$constant, $dst$$Register, $tmp$$Register);
|
|
%}
|
|
ins_pipe(loadConL);
|
|
%}
|
|
|
|
// Load long value from constant table (predicated by immL_expensive).
|
|
instruct loadConL_ldx(iRegL dst, immL_expensive con) %{
|
|
match(Set dst con);
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "LDX [$constanttablebase + $constantoffset],$dst\t! load from constant table: long=$con" %}
|
|
ins_encode %{
|
|
RegisterOrConstant con_offset = __ ensure_simm13_or_reg($constantoffset($con), $dst$$Register);
|
|
__ ldx($constanttablebase, con_offset, $dst$$Register);
|
|
%}
|
|
ins_pipe(loadConL);
|
|
%}
|
|
|
|
instruct loadConL0( iRegL dst, immL0 src ) %{
|
|
match(Set dst src);
|
|
ins_cost(DEFAULT_COST);
|
|
size(4);
|
|
format %{ "CLR $dst\t! long" %}
|
|
ins_encode( Set13( src, dst ) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct loadConL13( iRegL dst, immL13 src ) %{
|
|
match(Set dst src);
|
|
ins_cost(DEFAULT_COST * 2);
|
|
|
|
size(4);
|
|
format %{ "MOV $src,$dst\t! long" %}
|
|
ins_encode( Set13( src, dst ) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct loadConF(regF dst, immF con, o7RegI tmp) %{
|
|
match(Set dst con);
|
|
effect(KILL tmp);
|
|
format %{ "LDF [$constanttablebase + $constantoffset],$dst\t! load from constant table: float=$con" %}
|
|
ins_encode %{
|
|
RegisterOrConstant con_offset = __ ensure_simm13_or_reg($constantoffset($con), $tmp$$Register);
|
|
__ ldf(FloatRegisterImpl::S, $constanttablebase, con_offset, $dst$$FloatRegister);
|
|
%}
|
|
ins_pipe(loadConFD);
|
|
%}
|
|
|
|
instruct loadConD(regD dst, immD con, o7RegI tmp) %{
|
|
match(Set dst con);
|
|
effect(KILL tmp);
|
|
format %{ "LDDF [$constanttablebase + $constantoffset],$dst\t! load from constant table: double=$con" %}
|
|
ins_encode %{
|
|
// XXX This is a quick fix for 6833573.
|
|
//__ ldf(FloatRegisterImpl::D, $constanttablebase, $constantoffset($con), $dst$$FloatRegister);
|
|
RegisterOrConstant con_offset = __ ensure_simm13_or_reg($constantoffset($con), $tmp$$Register);
|
|
__ ldf(FloatRegisterImpl::D, $constanttablebase, con_offset, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(loadConFD);
|
|
%}
|
|
|
|
// Prefetch instructions for allocation.
|
|
// Must be safe to execute with invalid address (cannot fault).
|
|
|
|
instruct prefetchAlloc( memory mem ) %{
|
|
predicate(AllocatePrefetchInstr == 0);
|
|
match( PrefetchAllocation mem );
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "PREFETCH $mem,2\t! Prefetch allocation" %}
|
|
opcode(Assembler::prefetch_op3);
|
|
ins_encode( form3_mem_prefetch_write( mem ) );
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Use BIS instruction to prefetch for allocation.
|
|
// Could fault, need space at the end of TLAB.
|
|
instruct prefetchAlloc_bis( iRegP dst ) %{
|
|
predicate(AllocatePrefetchInstr == 1);
|
|
match( PrefetchAllocation dst );
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
|
|
format %{ "STXA [$dst]\t! // Prefetch allocation using BIS" %}
|
|
ins_encode %{
|
|
__ stxa(G0, $dst$$Register, G0, Assembler::ASI_ST_BLKINIT_PRIMARY);
|
|
%}
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
// Next code is used for finding next cache line address to prefetch.
|
|
instruct cacheLineAdr( iRegP dst, iRegP src, immL13 mask ) %{
|
|
match(Set dst (CastX2P (AndL (CastP2X src) mask)));
|
|
ins_cost(DEFAULT_COST);
|
|
size(4);
|
|
|
|
format %{ "AND $src,$mask,$dst\t! next cache line address" %}
|
|
ins_encode %{
|
|
__ and3($src$$Register, $mask$$constant, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
//----------Store Instructions-------------------------------------------------
|
|
// Store Byte
|
|
instruct storeB(memory mem, iRegI src) %{
|
|
match(Set mem (StoreB mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STB $src,$mem\t! byte" %}
|
|
opcode(Assembler::stb_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, src ) );
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
instruct storeB0(memory mem, immI0 src) %{
|
|
match(Set mem (StoreB mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STB $src,$mem\t! byte" %}
|
|
opcode(Assembler::stb_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, R_G0 ) );
|
|
ins_pipe(istore_mem_zero);
|
|
%}
|
|
|
|
instruct storeCM0(memory mem, immI0 src) %{
|
|
match(Set mem (StoreCM mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STB $src,$mem\t! CMS card-mark byte 0" %}
|
|
opcode(Assembler::stb_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, R_G0 ) );
|
|
ins_pipe(istore_mem_zero);
|
|
%}
|
|
|
|
// Store Char/Short
|
|
instruct storeC(memory mem, iRegI src) %{
|
|
match(Set mem (StoreC mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STH $src,$mem\t! short" %}
|
|
opcode(Assembler::sth_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, src ) );
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
instruct storeC0(memory mem, immI0 src) %{
|
|
match(Set mem (StoreC mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STH $src,$mem\t! short" %}
|
|
opcode(Assembler::sth_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, R_G0 ) );
|
|
ins_pipe(istore_mem_zero);
|
|
%}
|
|
|
|
// Store Integer
|
|
instruct storeI(memory mem, iRegI src) %{
|
|
match(Set mem (StoreI mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STW $src,$mem" %}
|
|
opcode(Assembler::stw_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, src ) );
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
// Store Long
|
|
instruct storeL(memory mem, iRegL src) %{
|
|
match(Set mem (StoreL mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "STX $src,$mem\t! long" %}
|
|
opcode(Assembler::stx_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, src ) );
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
instruct storeI0(memory mem, immI0 src) %{
|
|
match(Set mem (StoreI mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STW $src,$mem" %}
|
|
opcode(Assembler::stw_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, R_G0 ) );
|
|
ins_pipe(istore_mem_zero);
|
|
%}
|
|
|
|
instruct storeL0(memory mem, immL0 src) %{
|
|
match(Set mem (StoreL mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STX $src,$mem" %}
|
|
opcode(Assembler::stx_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, R_G0 ) );
|
|
ins_pipe(istore_mem_zero);
|
|
%}
|
|
|
|
// Store Integer from float register (used after fstoi)
|
|
instruct storeI_Freg(memory mem, regF src) %{
|
|
match(Set mem (StoreI mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STF $src,$mem\t! after fstoi/fdtoi" %}
|
|
opcode(Assembler::stf_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, src ) );
|
|
ins_pipe(fstoreF_mem_reg);
|
|
%}
|
|
|
|
// Store Pointer
|
|
instruct storeP(memory dst, sp_ptr_RegP src) %{
|
|
match(Set dst (StoreP dst src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STX $src,$dst\t! ptr" %}
|
|
opcode(Assembler::stx_op3, 0, REGP_OP);
|
|
ins_encode( form3_mem_reg( dst, src ) );
|
|
ins_pipe(istore_mem_spORreg);
|
|
%}
|
|
|
|
instruct storeP0(memory dst, immP0 src) %{
|
|
match(Set dst (StoreP dst src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STX $src,$dst\t! ptr" %}
|
|
opcode(Assembler::stx_op3, 0, REGP_OP);
|
|
ins_encode( form3_mem_reg( dst, R_G0 ) );
|
|
ins_pipe(istore_mem_zero);
|
|
%}
|
|
|
|
// Store Compressed Pointer
|
|
instruct storeN(memory dst, iRegN src) %{
|
|
match(Set dst (StoreN dst src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
|
|
format %{ "STW $src,$dst\t! compressed ptr" %}
|
|
ins_encode %{
|
|
Register base = as_Register($dst$$base);
|
|
Register index = as_Register($dst$$index);
|
|
Register src = $src$$Register;
|
|
if (index != G0) {
|
|
__ stw(src, base, index);
|
|
} else {
|
|
__ stw(src, base, $dst$$disp);
|
|
}
|
|
%}
|
|
ins_pipe(istore_mem_spORreg);
|
|
%}
|
|
|
|
instruct storeNKlass(memory dst, iRegN src) %{
|
|
match(Set dst (StoreNKlass dst src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
|
|
format %{ "STW $src,$dst\t! compressed klass ptr" %}
|
|
ins_encode %{
|
|
Register base = as_Register($dst$$base);
|
|
Register index = as_Register($dst$$index);
|
|
Register src = $src$$Register;
|
|
if (index != G0) {
|
|
__ stw(src, base, index);
|
|
} else {
|
|
__ stw(src, base, $dst$$disp);
|
|
}
|
|
%}
|
|
ins_pipe(istore_mem_spORreg);
|
|
%}
|
|
|
|
instruct storeN0(memory dst, immN0 src) %{
|
|
match(Set dst (StoreN dst src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
|
|
format %{ "STW $src,$dst\t! compressed ptr" %}
|
|
ins_encode %{
|
|
Register base = as_Register($dst$$base);
|
|
Register index = as_Register($dst$$index);
|
|
if (index != G0) {
|
|
__ stw(0, base, index);
|
|
} else {
|
|
__ stw(0, base, $dst$$disp);
|
|
}
|
|
%}
|
|
ins_pipe(istore_mem_zero);
|
|
%}
|
|
|
|
// Store Double
|
|
instruct storeD( memory mem, regD src) %{
|
|
match(Set mem (StoreD mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STDF $src,$mem" %}
|
|
opcode(Assembler::stdf_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, src ) );
|
|
ins_pipe(fstoreD_mem_reg);
|
|
%}
|
|
|
|
instruct storeD0( memory mem, immD0 src) %{
|
|
match(Set mem (StoreD mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STX $src,$mem" %}
|
|
opcode(Assembler::stx_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, R_G0 ) );
|
|
ins_pipe(fstoreD_mem_zero);
|
|
%}
|
|
|
|
// Store Float
|
|
instruct storeF( memory mem, regF src) %{
|
|
match(Set mem (StoreF mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STF $src,$mem" %}
|
|
opcode(Assembler::stf_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, src ) );
|
|
ins_pipe(fstoreF_mem_reg);
|
|
%}
|
|
|
|
instruct storeF0( memory mem, immF0 src) %{
|
|
match(Set mem (StoreF mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STW $src,$mem\t! storeF0" %}
|
|
opcode(Assembler::stw_op3);
|
|
ins_encode(simple_form3_mem_reg( mem, R_G0 ) );
|
|
ins_pipe(fstoreF_mem_zero);
|
|
%}
|
|
|
|
// Convert oop pointer into compressed form
|
|
instruct encodeHeapOop(iRegN dst, iRegP src) %{
|
|
predicate(n->bottom_type()->make_ptr()->ptr() != TypePtr::NotNull);
|
|
match(Set dst (EncodeP src));
|
|
format %{ "encode_heap_oop $src, $dst" %}
|
|
ins_encode %{
|
|
__ encode_heap_oop($src$$Register, $dst$$Register);
|
|
%}
|
|
ins_avoid_back_to_back(Universe::narrow_oop_base() == NULL ? AVOID_NONE : AVOID_BEFORE);
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct encodeHeapOop_not_null(iRegN dst, iRegP src) %{
|
|
predicate(n->bottom_type()->make_ptr()->ptr() == TypePtr::NotNull);
|
|
match(Set dst (EncodeP src));
|
|
format %{ "encode_heap_oop_not_null $src, $dst" %}
|
|
ins_encode %{
|
|
__ encode_heap_oop_not_null($src$$Register, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct decodeHeapOop(iRegP dst, iRegN src) %{
|
|
predicate(n->bottom_type()->is_oopptr()->ptr() != TypePtr::NotNull &&
|
|
n->bottom_type()->is_oopptr()->ptr() != TypePtr::Constant);
|
|
match(Set dst (DecodeN src));
|
|
format %{ "decode_heap_oop $src, $dst" %}
|
|
ins_encode %{
|
|
__ decode_heap_oop($src$$Register, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct decodeHeapOop_not_null(iRegP dst, iRegN src) %{
|
|
predicate(n->bottom_type()->is_oopptr()->ptr() == TypePtr::NotNull ||
|
|
n->bottom_type()->is_oopptr()->ptr() == TypePtr::Constant);
|
|
match(Set dst (DecodeN src));
|
|
format %{ "decode_heap_oop_not_null $src, $dst" %}
|
|
ins_encode %{
|
|
__ decode_heap_oop_not_null($src$$Register, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct encodeKlass_not_null(iRegN dst, iRegP src) %{
|
|
match(Set dst (EncodePKlass src));
|
|
format %{ "encode_klass_not_null $src, $dst" %}
|
|
ins_encode %{
|
|
__ encode_klass_not_null($src$$Register, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct decodeKlass_not_null(iRegP dst, iRegN src) %{
|
|
match(Set dst (DecodeNKlass src));
|
|
format %{ "decode_klass_not_null $src, $dst" %}
|
|
ins_encode %{
|
|
__ decode_klass_not_null($src$$Register, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
//----------MemBar Instructions-----------------------------------------------
|
|
// Memory barrier flavors
|
|
|
|
instruct membar_acquire() %{
|
|
match(MemBarAcquire);
|
|
match(LoadFence);
|
|
ins_cost(4*MEMORY_REF_COST);
|
|
|
|
size(0);
|
|
format %{ "MEMBAR-acquire" %}
|
|
ins_encode( enc_membar_acquire );
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct membar_acquire_lock() %{
|
|
match(MemBarAcquireLock);
|
|
ins_cost(0);
|
|
|
|
size(0);
|
|
format %{ "!MEMBAR-acquire (CAS in prior FastLock so empty encoding)" %}
|
|
ins_encode( );
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
instruct membar_release() %{
|
|
match(MemBarRelease);
|
|
match(StoreFence);
|
|
ins_cost(4*MEMORY_REF_COST);
|
|
|
|
size(0);
|
|
format %{ "MEMBAR-release" %}
|
|
ins_encode( enc_membar_release );
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct membar_release_lock() %{
|
|
match(MemBarReleaseLock);
|
|
ins_cost(0);
|
|
|
|
size(0);
|
|
format %{ "!MEMBAR-release (CAS in succeeding FastUnlock so empty encoding)" %}
|
|
ins_encode( );
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
instruct membar_volatile() %{
|
|
match(MemBarVolatile);
|
|
ins_cost(4*MEMORY_REF_COST);
|
|
|
|
size(4);
|
|
format %{ "MEMBAR-volatile" %}
|
|
ins_encode( enc_membar_volatile );
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct unnecessary_membar_volatile() %{
|
|
match(MemBarVolatile);
|
|
predicate(Matcher::post_store_load_barrier(n));
|
|
ins_cost(0);
|
|
|
|
size(0);
|
|
format %{ "!MEMBAR-volatile (unnecessary so empty encoding)" %}
|
|
ins_encode( );
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
instruct membar_storestore() %{
|
|
match(MemBarStoreStore);
|
|
ins_cost(0);
|
|
|
|
size(0);
|
|
format %{ "!MEMBAR-storestore (empty encoding)" %}
|
|
ins_encode( );
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
//----------Register Move Instructions-----------------------------------------
|
|
instruct roundDouble_nop(regD dst) %{
|
|
match(Set dst (RoundDouble dst));
|
|
ins_cost(0);
|
|
// SPARC results are already "rounded" (i.e., normal-format IEEE)
|
|
ins_encode( );
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
|
|
instruct roundFloat_nop(regF dst) %{
|
|
match(Set dst (RoundFloat dst));
|
|
ins_cost(0);
|
|
// SPARC results are already "rounded" (i.e., normal-format IEEE)
|
|
ins_encode( );
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
|
|
// Cast Index to Pointer for unsafe natives
|
|
instruct castX2P(iRegX src, iRegP dst) %{
|
|
match(Set dst (CastX2P src));
|
|
|
|
format %{ "MOV $src,$dst\t! IntX->Ptr" %}
|
|
ins_encode( form3_g0_rs2_rd_move( src, dst ) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Cast Pointer to Index for unsafe natives
|
|
instruct castP2X(iRegP src, iRegX dst) %{
|
|
match(Set dst (CastP2X src));
|
|
|
|
format %{ "MOV $src,$dst\t! Ptr->IntX" %}
|
|
ins_encode( form3_g0_rs2_rd_move( src, dst ) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct stfSSD(stackSlotD stkSlot, regD src) %{
|
|
// %%%% TO DO: Tell the coalescer that this kind of node is a copy!
|
|
match(Set stkSlot src); // chain rule
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "STDF $src,$stkSlot\t!stk" %}
|
|
opcode(Assembler::stdf_op3);
|
|
ins_encode(simple_form3_mem_reg(stkSlot, src));
|
|
ins_pipe(fstoreD_stk_reg);
|
|
%}
|
|
|
|
instruct ldfSSD(regD dst, stackSlotD stkSlot) %{
|
|
// %%%% TO DO: Tell the coalescer that this kind of node is a copy!
|
|
match(Set dst stkSlot); // chain rule
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "LDDF $stkSlot,$dst\t!stk" %}
|
|
opcode(Assembler::lddf_op3);
|
|
ins_encode(simple_form3_mem_reg(stkSlot, dst));
|
|
ins_pipe(floadD_stk);
|
|
%}
|
|
|
|
instruct stfSSF(stackSlotF stkSlot, regF src) %{
|
|
// %%%% TO DO: Tell the coalescer that this kind of node is a copy!
|
|
match(Set stkSlot src); // chain rule
|
|
ins_cost(MEMORY_REF_COST);
|
|
format %{ "STF $src,$stkSlot\t!stk" %}
|
|
opcode(Assembler::stf_op3);
|
|
ins_encode(simple_form3_mem_reg(stkSlot, src));
|
|
ins_pipe(fstoreF_stk_reg);
|
|
%}
|
|
|
|
//----------Conditional Move---------------------------------------------------
|
|
// Conditional move
|
|
instruct cmovIP_reg(cmpOpP cmp, flagsRegP pcc, iRegI dst, iRegI src) %{
|
|
match(Set dst (CMoveI (Binary cmp pcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
format %{ "MOV$cmp $pcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::ptr_cc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovIP_imm(cmpOpP cmp, flagsRegP pcc, iRegI dst, immI11 src) %{
|
|
match(Set dst (CMoveI (Binary cmp pcc) (Binary dst src)));
|
|
ins_cost(140);
|
|
format %{ "MOV$cmp $pcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::ptr_cc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovII_reg(cmpOp cmp, flagsReg icc, iRegI dst, iRegI src) %{
|
|
match(Set dst (CMoveI (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovII_imm(cmpOp cmp, flagsReg icc, iRegI dst, immI11 src) %{
|
|
match(Set dst (CMoveI (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(140);
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovIIu_reg(cmpOpU cmp, flagsRegU icc, iRegI dst, iRegI src) %{
|
|
match(Set dst (CMoveI (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovIIu_imm(cmpOpU cmp, flagsRegU icc, iRegI dst, immI11 src) %{
|
|
match(Set dst (CMoveI (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(140);
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovIF_reg(cmpOpF cmp, flagsRegF fcc, iRegI dst, iRegI src) %{
|
|
match(Set dst (CMoveI (Binary cmp fcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
format %{ "MOV$cmp $fcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg_f(cmp,dst,src, fcc) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovIF_imm(cmpOpF cmp, flagsRegF fcc, iRegI dst, immI11 src) %{
|
|
match(Set dst (CMoveI (Binary cmp fcc) (Binary dst src)));
|
|
ins_cost(140);
|
|
size(4);
|
|
format %{ "MOV$cmp $fcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_imm_f(cmp,dst,src, fcc) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
// Conditional move for RegN. Only cmov(reg,reg).
|
|
instruct cmovNP_reg(cmpOpP cmp, flagsRegP pcc, iRegN dst, iRegN src) %{
|
|
match(Set dst (CMoveN (Binary cmp pcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
format %{ "MOV$cmp $pcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::ptr_cc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// This instruction also works with CmpN so we don't need cmovNN_reg.
|
|
instruct cmovNI_reg(cmpOp cmp, flagsReg icc, iRegN dst, iRegN src) %{
|
|
match(Set dst (CMoveN (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// This instruction also works with CmpN so we don't need cmovNN_reg.
|
|
instruct cmovNIu_reg(cmpOpU cmp, flagsRegU icc, iRegN dst, iRegN src) %{
|
|
match(Set dst (CMoveN (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovNF_reg(cmpOpF cmp, flagsRegF fcc, iRegN dst, iRegN src) %{
|
|
match(Set dst (CMoveN (Binary cmp fcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
format %{ "MOV$cmp $fcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg_f(cmp,dst,src, fcc) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Conditional move
|
|
instruct cmovPP_reg(cmpOpP cmp, flagsRegP pcc, iRegP dst, iRegP src) %{
|
|
match(Set dst (CMoveP (Binary cmp pcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
format %{ "MOV$cmp $pcc,$src,$dst\t! ptr" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::ptr_cc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovPP_imm(cmpOpP cmp, flagsRegP pcc, iRegP dst, immP0 src) %{
|
|
match(Set dst (CMoveP (Binary cmp pcc) (Binary dst src)));
|
|
ins_cost(140);
|
|
format %{ "MOV$cmp $pcc,$src,$dst\t! ptr" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::ptr_cc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
// This instruction also works with CmpN so we don't need cmovPN_reg.
|
|
instruct cmovPI_reg(cmpOp cmp, flagsReg icc, iRegP dst, iRegP src) %{
|
|
match(Set dst (CMoveP (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst\t! ptr" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovPIu_reg(cmpOpU cmp, flagsRegU icc, iRegP dst, iRegP src) %{
|
|
match(Set dst (CMoveP (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst\t! ptr" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovPI_imm(cmpOp cmp, flagsReg icc, iRegP dst, immP0 src) %{
|
|
match(Set dst (CMoveP (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(140);
|
|
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst\t! ptr" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovPIu_imm(cmpOpU cmp, flagsRegU icc, iRegP dst, immP0 src) %{
|
|
match(Set dst (CMoveP (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(140);
|
|
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst\t! ptr" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovPF_reg(cmpOpF cmp, flagsRegF fcc, iRegP dst, iRegP src) %{
|
|
match(Set dst (CMoveP (Binary cmp fcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
format %{ "MOV$cmp $fcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg_f(cmp,dst,src, fcc) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovPF_imm(cmpOpF cmp, flagsRegF fcc, iRegP dst, immP0 src) %{
|
|
match(Set dst (CMoveP (Binary cmp fcc) (Binary dst src)));
|
|
ins_cost(140);
|
|
size(4);
|
|
format %{ "MOV$cmp $fcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_imm_f(cmp,dst,src, fcc) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
// Conditional move
|
|
instruct cmovFP_reg(cmpOpP cmp, flagsRegP pcc, regF dst, regF src) %{
|
|
match(Set dst (CMoveF (Binary cmp pcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
opcode(0x101);
|
|
format %{ "FMOVD$cmp $pcc,$src,$dst" %}
|
|
ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::ptr_cc)) );
|
|
ins_pipe(int_conditional_float_move);
|
|
%}
|
|
|
|
instruct cmovFI_reg(cmpOp cmp, flagsReg icc, regF dst, regF src) %{
|
|
match(Set dst (CMoveF (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
|
|
size(4);
|
|
format %{ "FMOVS$cmp $icc,$src,$dst" %}
|
|
opcode(0x101);
|
|
ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(int_conditional_float_move);
|
|
%}
|
|
|
|
instruct cmovFIu_reg(cmpOpU cmp, flagsRegU icc, regF dst, regF src) %{
|
|
match(Set dst (CMoveF (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
|
|
size(4);
|
|
format %{ "FMOVS$cmp $icc,$src,$dst" %}
|
|
opcode(0x101);
|
|
ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(int_conditional_float_move);
|
|
%}
|
|
|
|
// Conditional move,
|
|
instruct cmovFF_reg(cmpOpF cmp, flagsRegF fcc, regF dst, regF src) %{
|
|
match(Set dst (CMoveF (Binary cmp fcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
format %{ "FMOVF$cmp $fcc,$src,$dst" %}
|
|
opcode(0x1);
|
|
ins_encode( enc_cmovff_reg(cmp,fcc,dst,src) );
|
|
ins_pipe(int_conditional_double_move);
|
|
%}
|
|
|
|
// Conditional move
|
|
instruct cmovDP_reg(cmpOpP cmp, flagsRegP pcc, regD dst, regD src) %{
|
|
match(Set dst (CMoveD (Binary cmp pcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
opcode(0x102);
|
|
format %{ "FMOVD$cmp $pcc,$src,$dst" %}
|
|
ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::ptr_cc)) );
|
|
ins_pipe(int_conditional_double_move);
|
|
%}
|
|
|
|
instruct cmovDI_reg(cmpOp cmp, flagsReg icc, regD dst, regD src) %{
|
|
match(Set dst (CMoveD (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
|
|
size(4);
|
|
format %{ "FMOVD$cmp $icc,$src,$dst" %}
|
|
opcode(0x102);
|
|
ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(int_conditional_double_move);
|
|
%}
|
|
|
|
instruct cmovDIu_reg(cmpOpU cmp, flagsRegU icc, regD dst, regD src) %{
|
|
match(Set dst (CMoveD (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
|
|
size(4);
|
|
format %{ "FMOVD$cmp $icc,$src,$dst" %}
|
|
opcode(0x102);
|
|
ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(int_conditional_double_move);
|
|
%}
|
|
|
|
// Conditional move,
|
|
instruct cmovDF_reg(cmpOpF cmp, flagsRegF fcc, regD dst, regD src) %{
|
|
match(Set dst (CMoveD (Binary cmp fcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
size(4);
|
|
format %{ "FMOVD$cmp $fcc,$src,$dst" %}
|
|
opcode(0x2);
|
|
ins_encode( enc_cmovff_reg(cmp,fcc,dst,src) );
|
|
ins_pipe(int_conditional_double_move);
|
|
%}
|
|
|
|
// Conditional move
|
|
instruct cmovLP_reg(cmpOpP cmp, flagsRegP pcc, iRegL dst, iRegL src) %{
|
|
match(Set dst (CMoveL (Binary cmp pcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
format %{ "MOV$cmp $pcc,$src,$dst\t! long" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::ptr_cc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovLP_imm(cmpOpP cmp, flagsRegP pcc, iRegL dst, immI11 src) %{
|
|
match(Set dst (CMoveL (Binary cmp pcc) (Binary dst src)));
|
|
ins_cost(140);
|
|
format %{ "MOV$cmp $pcc,$src,$dst\t! long" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::ptr_cc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovLI_reg(cmpOp cmp, flagsReg icc, iRegL dst, iRegL src) %{
|
|
match(Set dst (CMoveL (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst\t! long" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
|
|
instruct cmovLIu_reg(cmpOpU cmp, flagsRegU icc, iRegL dst, iRegL src) %{
|
|
match(Set dst (CMoveL (Binary cmp icc) (Binary dst src)));
|
|
ins_cost(150);
|
|
|
|
size(4);
|
|
format %{ "MOV$cmp $icc,$src,$dst\t! long" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::icc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
|
|
instruct cmovLF_reg(cmpOpF cmp, flagsRegF fcc, iRegL dst, iRegL src) %{
|
|
match(Set dst (CMoveL (Binary cmp fcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
|
|
size(4);
|
|
format %{ "MOV$cmp $fcc,$src,$dst\t! long" %}
|
|
ins_encode( enc_cmov_reg_f(cmp,dst,src, fcc) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
|
|
|
|
//----------OS and Locking Instructions----------------------------------------
|
|
|
|
// This name is KNOWN by the ADLC and cannot be changed.
|
|
// The ADLC forces a 'TypeRawPtr::BOTTOM' output type
|
|
// for this guy.
|
|
instruct tlsLoadP(g2RegP dst) %{
|
|
match(Set dst (ThreadLocal));
|
|
|
|
size(0);
|
|
ins_cost(0);
|
|
format %{ "# TLS is in G2" %}
|
|
ins_encode( /*empty encoding*/ );
|
|
ins_pipe(ialu_none);
|
|
%}
|
|
|
|
instruct checkCastPP( iRegP dst ) %{
|
|
match(Set dst (CheckCastPP dst));
|
|
|
|
size(0);
|
|
format %{ "# checkcastPP of $dst" %}
|
|
ins_encode( /*empty encoding*/ );
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
|
|
instruct castPP( iRegP dst ) %{
|
|
match(Set dst (CastPP dst));
|
|
format %{ "# castPP of $dst" %}
|
|
ins_encode( /*empty encoding*/ );
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
instruct castII( iRegI dst ) %{
|
|
match(Set dst (CastII dst));
|
|
format %{ "# castII of $dst" %}
|
|
ins_encode( /*empty encoding*/ );
|
|
ins_cost(0);
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
//----------Arithmetic Instructions--------------------------------------------
|
|
// Addition Instructions
|
|
// Register Addition
|
|
instruct addI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
match(Set dst (AddI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "ADD $src1,$src2,$dst" %}
|
|
ins_encode %{
|
|
__ add($src1$$Register, $src2$$Register, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Immediate Addition
|
|
instruct addI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{
|
|
match(Set dst (AddI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "ADD $src1,$src2,$dst" %}
|
|
opcode(Assembler::add_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Pointer Register Addition
|
|
instruct addP_reg_reg(iRegP dst, iRegP src1, iRegX src2) %{
|
|
match(Set dst (AddP src1 src2));
|
|
|
|
size(4);
|
|
format %{ "ADD $src1,$src2,$dst" %}
|
|
opcode(Assembler::add_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Pointer Immediate Addition
|
|
instruct addP_reg_imm13(iRegP dst, iRegP src1, immX13 src2) %{
|
|
match(Set dst (AddP src1 src2));
|
|
|
|
size(4);
|
|
format %{ "ADD $src1,$src2,$dst" %}
|
|
opcode(Assembler::add_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Long Addition
|
|
instruct addL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
match(Set dst (AddL src1 src2));
|
|
|
|
size(4);
|
|
format %{ "ADD $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::add_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
instruct addL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{
|
|
match(Set dst (AddL src1 con));
|
|
|
|
size(4);
|
|
format %{ "ADD $src1,$con,$dst" %}
|
|
opcode(Assembler::add_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, con, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
//----------Conditional_store--------------------------------------------------
|
|
// Conditional-store of the updated heap-top.
|
|
// Used during allocation of the shared heap.
|
|
// Sets flags (EQ) on success. Implemented with a CASA on Sparc.
|
|
|
|
// LoadP-locked. Same as a regular pointer load when used with a compare-swap
|
|
instruct loadPLocked(iRegP dst, memory mem) %{
|
|
match(Set dst (LoadPLocked mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDX $mem,$dst\t! ptr" %}
|
|
opcode(Assembler::ldx_op3, 0, REGP_OP);
|
|
ins_encode( form3_mem_reg( mem, dst ) );
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
instruct storePConditional( iRegP heap_top_ptr, iRegP oldval, g3RegP newval, flagsRegP pcc ) %{
|
|
match(Set pcc (StorePConditional heap_top_ptr (Binary oldval newval)));
|
|
effect( KILL newval );
|
|
format %{ "CASA [$heap_top_ptr],$oldval,R_G3\t! If $oldval==[$heap_top_ptr] Then store R_G3 into [$heap_top_ptr], set R_G3=[$heap_top_ptr] in any case\n\t"
|
|
"CMP R_G3,$oldval\t\t! See if we made progress" %}
|
|
ins_encode( enc_cas(heap_top_ptr,oldval,newval) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
// Conditional-store of an int value.
|
|
instruct storeIConditional( iRegP mem_ptr, iRegI oldval, g3RegI newval, flagsReg icc ) %{
|
|
match(Set icc (StoreIConditional mem_ptr (Binary oldval newval)));
|
|
effect( KILL newval );
|
|
format %{ "CASA [$mem_ptr],$oldval,$newval\t! If $oldval==[$mem_ptr] Then store $newval into [$mem_ptr], set $newval=[$mem_ptr] in any case\n\t"
|
|
"CMP $oldval,$newval\t\t! See if we made progress" %}
|
|
ins_encode( enc_cas(mem_ptr,oldval,newval) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
// Conditional-store of a long value.
|
|
instruct storeLConditional( iRegP mem_ptr, iRegL oldval, g3RegL newval, flagsRegL xcc ) %{
|
|
match(Set xcc (StoreLConditional mem_ptr (Binary oldval newval)));
|
|
effect( KILL newval );
|
|
format %{ "CASXA [$mem_ptr],$oldval,$newval\t! If $oldval==[$mem_ptr] Then store $newval into [$mem_ptr], set $newval=[$mem_ptr] in any case\n\t"
|
|
"CMP $oldval,$newval\t\t! See if we made progress" %}
|
|
ins_encode( enc_cas(mem_ptr,oldval,newval) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
// No flag versions for CompareAndSwap{P,I,L} because matcher can't match them
|
|
|
|
instruct compareAndSwapL_bool(iRegP mem_ptr, iRegL oldval, iRegL newval, iRegI res, o7RegI tmp1, flagsReg ccr ) %{
|
|
predicate(VM_Version::supports_cx8());
|
|
match(Set res (CompareAndSwapL mem_ptr (Binary oldval newval)));
|
|
match(Set res (WeakCompareAndSwapL mem_ptr (Binary oldval newval)));
|
|
effect( USE mem_ptr, KILL ccr, KILL tmp1);
|
|
format %{
|
|
"MOV $newval,O7\n\t"
|
|
"CASXA [$mem_ptr],$oldval,O7\t! If $oldval==[$mem_ptr] Then store O7 into [$mem_ptr], set O7=[$mem_ptr] in any case\n\t"
|
|
"CMP $oldval,O7\t\t! See if we made progress\n\t"
|
|
"MOV 1,$res\n\t"
|
|
"MOVne xcc,R_G0,$res"
|
|
%}
|
|
ins_encode( enc_casx(mem_ptr, oldval, newval),
|
|
enc_lflags_ne_to_boolean(res) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
|
|
instruct compareAndSwapI_bool(iRegP mem_ptr, iRegI oldval, iRegI newval, iRegI res, o7RegI tmp1, flagsReg ccr ) %{
|
|
match(Set res (CompareAndSwapI mem_ptr (Binary oldval newval)));
|
|
match(Set res (WeakCompareAndSwapI mem_ptr (Binary oldval newval)));
|
|
effect( USE mem_ptr, KILL ccr, KILL tmp1);
|
|
format %{
|
|
"MOV $newval,O7\n\t"
|
|
"CASA [$mem_ptr],$oldval,O7\t! If $oldval==[$mem_ptr] Then store O7 into [$mem_ptr], set O7=[$mem_ptr] in any case\n\t"
|
|
"CMP $oldval,O7\t\t! See if we made progress\n\t"
|
|
"MOV 1,$res\n\t"
|
|
"MOVne icc,R_G0,$res"
|
|
%}
|
|
ins_encode( enc_casi(mem_ptr, oldval, newval),
|
|
enc_iflags_ne_to_boolean(res) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
instruct compareAndSwapP_bool(iRegP mem_ptr, iRegP oldval, iRegP newval, iRegI res, o7RegI tmp1, flagsReg ccr ) %{
|
|
predicate(VM_Version::supports_cx8());
|
|
match(Set res (CompareAndSwapP mem_ptr (Binary oldval newval)));
|
|
match(Set res (WeakCompareAndSwapP mem_ptr (Binary oldval newval)));
|
|
effect( USE mem_ptr, KILL ccr, KILL tmp1);
|
|
format %{
|
|
"MOV $newval,O7\n\t"
|
|
"CASA_PTR [$mem_ptr],$oldval,O7\t! If $oldval==[$mem_ptr] Then store O7 into [$mem_ptr], set O7=[$mem_ptr] in any case\n\t"
|
|
"CMP $oldval,O7\t\t! See if we made progress\n\t"
|
|
"MOV 1,$res\n\t"
|
|
"MOVne xcc,R_G0,$res"
|
|
%}
|
|
ins_encode( enc_casx(mem_ptr, oldval, newval),
|
|
enc_lflags_ne_to_boolean(res) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
instruct compareAndSwapN_bool(iRegP mem_ptr, iRegN oldval, iRegN newval, iRegI res, o7RegI tmp1, flagsReg ccr ) %{
|
|
match(Set res (CompareAndSwapN mem_ptr (Binary oldval newval)));
|
|
match(Set res (WeakCompareAndSwapN mem_ptr (Binary oldval newval)));
|
|
effect( USE mem_ptr, KILL ccr, KILL tmp1);
|
|
format %{
|
|
"MOV $newval,O7\n\t"
|
|
"CASA [$mem_ptr],$oldval,O7\t! If $oldval==[$mem_ptr] Then store O7 into [$mem_ptr], set O7=[$mem_ptr] in any case\n\t"
|
|
"CMP $oldval,O7\t\t! See if we made progress\n\t"
|
|
"MOV 1,$res\n\t"
|
|
"MOVne icc,R_G0,$res"
|
|
%}
|
|
ins_encode( enc_casi(mem_ptr, oldval, newval),
|
|
enc_iflags_ne_to_boolean(res) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
instruct compareAndExchangeI(iRegP mem_ptr, iRegI oldval, iRegI newval)
|
|
%{
|
|
match(Set newval (CompareAndExchangeI mem_ptr (Binary oldval newval)));
|
|
effect( USE mem_ptr );
|
|
|
|
format %{
|
|
"CASA [$mem_ptr],$oldval,$newval\t! If $oldval==[$mem_ptr] Then store $newval into [$mem_ptr] and set $newval=[$mem_ptr]\n\t"
|
|
%}
|
|
ins_encode( enc_casi_exch(mem_ptr, oldval, newval) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
instruct compareAndExchangeL(iRegP mem_ptr, iRegL oldval, iRegL newval)
|
|
%{
|
|
match(Set newval (CompareAndExchangeL mem_ptr (Binary oldval newval)));
|
|
effect( USE mem_ptr );
|
|
|
|
format %{
|
|
"CASXA [$mem_ptr],$oldval,$newval\t! If $oldval==[$mem_ptr] Then store $newval into [$mem_ptr] and set $newval=[$mem_ptr]\n\t"
|
|
%}
|
|
ins_encode( enc_casx_exch(mem_ptr, oldval, newval) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
instruct compareAndExchangeP(iRegP mem_ptr, iRegP oldval, iRegP newval)
|
|
%{
|
|
match(Set newval (CompareAndExchangeP mem_ptr (Binary oldval newval)));
|
|
effect( USE mem_ptr );
|
|
|
|
format %{
|
|
"CASXA [$mem_ptr],$oldval,$newval\t! If $oldval==[$mem_ptr] Then store $newval into [$mem_ptr] and set $newval=[$mem_ptr]\n\t"
|
|
%}
|
|
ins_encode( enc_casx_exch(mem_ptr, oldval, newval) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
instruct compareAndExchangeN(iRegP mem_ptr, iRegN oldval, iRegN newval)
|
|
%{
|
|
match(Set newval (CompareAndExchangeN mem_ptr (Binary oldval newval)));
|
|
effect( USE mem_ptr );
|
|
|
|
format %{
|
|
"CASA [$mem_ptr],$oldval,$newval\t! If $oldval==[$mem_ptr] Then store $newval into [$mem_ptr] and set $newval=[$mem_ptr]\n\t"
|
|
%}
|
|
ins_encode( enc_casi_exch(mem_ptr, oldval, newval) );
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
instruct xchgI( memory mem, iRegI newval) %{
|
|
match(Set newval (GetAndSetI mem newval));
|
|
format %{ "SWAP [$mem],$newval" %}
|
|
size(4);
|
|
ins_encode %{
|
|
__ swap($mem$$Address, $newval$$Register);
|
|
%}
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
|
|
instruct xchgN( memory mem, iRegN newval) %{
|
|
match(Set newval (GetAndSetN mem newval));
|
|
format %{ "SWAP [$mem],$newval" %}
|
|
size(4);
|
|
ins_encode %{
|
|
__ swap($mem$$Address, $newval$$Register);
|
|
%}
|
|
ins_pipe( long_memory_op );
|
|
%}
|
|
|
|
//---------------------
|
|
// Subtraction Instructions
|
|
// Register Subtraction
|
|
instruct subI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
match(Set dst (SubI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SUB $src1,$src2,$dst" %}
|
|
opcode(Assembler::sub_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Immediate Subtraction
|
|
instruct subI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{
|
|
match(Set dst (SubI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SUB $src1,$src2,$dst" %}
|
|
opcode(Assembler::sub_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
instruct subI_zero_reg(iRegI dst, immI0 zero, iRegI src2) %{
|
|
match(Set dst (SubI zero src2));
|
|
|
|
size(4);
|
|
format %{ "NEG $src2,$dst" %}
|
|
opcode(Assembler::sub_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( R_G0, src2, dst ) );
|
|
ins_pipe(ialu_zero_reg);
|
|
%}
|
|
|
|
// Long subtraction
|
|
instruct subL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
match(Set dst (SubL src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SUB $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::sub_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Immediate Subtraction
|
|
instruct subL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{
|
|
match(Set dst (SubL src1 con));
|
|
|
|
size(4);
|
|
format %{ "SUB $src1,$con,$dst\t! long" %}
|
|
opcode(Assembler::sub_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, con, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Long negation
|
|
instruct negL_reg_reg(iRegL dst, immL0 zero, iRegL src2) %{
|
|
match(Set dst (SubL zero src2));
|
|
|
|
size(4);
|
|
format %{ "NEG $src2,$dst\t! long" %}
|
|
opcode(Assembler::sub_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( R_G0, src2, dst ) );
|
|
ins_pipe(ialu_zero_reg);
|
|
%}
|
|
|
|
// Multiplication Instructions
|
|
// Integer Multiplication
|
|
// Register Multiplication
|
|
instruct mulI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
match(Set dst (MulI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "MULX $src1,$src2,$dst" %}
|
|
opcode(Assembler::mulx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(imul_reg_reg);
|
|
%}
|
|
|
|
// Immediate Multiplication
|
|
instruct mulI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{
|
|
match(Set dst (MulI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "MULX $src1,$src2,$dst" %}
|
|
opcode(Assembler::mulx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(imul_reg_imm);
|
|
%}
|
|
|
|
instruct mulL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
match(Set dst (MulL src1 src2));
|
|
ins_cost(DEFAULT_COST * 5);
|
|
size(4);
|
|
format %{ "MULX $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::mulx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(mulL_reg_reg);
|
|
%}
|
|
|
|
// Immediate Multiplication
|
|
instruct mulL_reg_imm13(iRegL dst, iRegL src1, immL13 src2) %{
|
|
match(Set dst (MulL src1 src2));
|
|
ins_cost(DEFAULT_COST * 5);
|
|
size(4);
|
|
format %{ "MULX $src1,$src2,$dst" %}
|
|
opcode(Assembler::mulx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(mulL_reg_imm);
|
|
%}
|
|
|
|
// Integer Division
|
|
// Register Division
|
|
instruct divI_reg_reg(iRegI dst, iRegIsafe src1, iRegIsafe src2) %{
|
|
match(Set dst (DivI src1 src2));
|
|
ins_cost((2+71)*DEFAULT_COST);
|
|
|
|
format %{ "SRA $src2,0,$src2\n\t"
|
|
"SRA $src1,0,$src1\n\t"
|
|
"SDIVX $src1,$src2,$dst" %}
|
|
ins_encode( idiv_reg( src1, src2, dst ) );
|
|
ins_pipe(sdiv_reg_reg);
|
|
%}
|
|
|
|
// Immediate Division
|
|
instruct divI_reg_imm13(iRegI dst, iRegIsafe src1, immI13 src2) %{
|
|
match(Set dst (DivI src1 src2));
|
|
ins_cost((2+71)*DEFAULT_COST);
|
|
|
|
format %{ "SRA $src1,0,$src1\n\t"
|
|
"SDIVX $src1,$src2,$dst" %}
|
|
ins_encode( idiv_imm( src1, src2, dst ) );
|
|
ins_pipe(sdiv_reg_imm);
|
|
%}
|
|
|
|
//----------Div-By-10-Expansion------------------------------------------------
|
|
// Extract hi bits of a 32x32->64 bit multiply.
|
|
// Expand rule only, not matched
|
|
instruct mul_hi(iRegIsafe dst, iRegIsafe src1, iRegIsafe src2 ) %{
|
|
effect( DEF dst, USE src1, USE src2 );
|
|
format %{ "MULX $src1,$src2,$dst\t! Used in div-by-10\n\t"
|
|
"SRLX $dst,#32,$dst\t\t! Extract only hi word of result" %}
|
|
ins_encode( enc_mul_hi(dst,src1,src2));
|
|
ins_pipe(sdiv_reg_reg);
|
|
%}
|
|
|
|
// Magic constant, reciprocal of 10
|
|
instruct loadConI_x66666667(iRegIsafe dst) %{
|
|
effect( DEF dst );
|
|
|
|
size(8);
|
|
format %{ "SET 0x66666667,$dst\t! Used in div-by-10" %}
|
|
ins_encode( Set32(0x66666667, dst) );
|
|
ins_pipe(ialu_hi_lo_reg);
|
|
%}
|
|
|
|
// Register Shift Right Arithmetic Long by 32-63
|
|
instruct sra_31( iRegI dst, iRegI src ) %{
|
|
effect( DEF dst, USE src );
|
|
format %{ "SRA $src,31,$dst\t! Used in div-by-10" %}
|
|
ins_encode( form3_rs1_rd_copysign_hi(src,dst) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Arithmetic Shift Right by 8-bit immediate
|
|
instruct sra_reg_2( iRegI dst, iRegI src ) %{
|
|
effect( DEF dst, USE src );
|
|
format %{ "SRA $src,2,$dst\t! Used in div-by-10" %}
|
|
opcode(Assembler::sra_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src, 0x2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Integer DIV with 10
|
|
instruct divI_10( iRegI dst, iRegIsafe src, immI10 div ) %{
|
|
match(Set dst (DivI src div));
|
|
ins_cost((6+6)*DEFAULT_COST);
|
|
expand %{
|
|
iRegIsafe tmp1; // Killed temps;
|
|
iRegIsafe tmp2; // Killed temps;
|
|
iRegI tmp3; // Killed temps;
|
|
iRegI tmp4; // Killed temps;
|
|
loadConI_x66666667( tmp1 ); // SET 0x66666667 -> tmp1
|
|
mul_hi( tmp2, src, tmp1 ); // MUL hibits(src * tmp1) -> tmp2
|
|
sra_31( tmp3, src ); // SRA src,31 -> tmp3
|
|
sra_reg_2( tmp4, tmp2 ); // SRA tmp2,2 -> tmp4
|
|
subI_reg_reg( dst,tmp4,tmp3); // SUB tmp4 - tmp3 -> dst
|
|
%}
|
|
%}
|
|
|
|
// Register Long Division
|
|
instruct divL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
match(Set dst (DivL src1 src2));
|
|
ins_cost(DEFAULT_COST*71);
|
|
size(4);
|
|
format %{ "SDIVX $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::sdivx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(divL_reg_reg);
|
|
%}
|
|
|
|
// Register Long Division
|
|
instruct divL_reg_imm13(iRegL dst, iRegL src1, immL13 src2) %{
|
|
match(Set dst (DivL src1 src2));
|
|
ins_cost(DEFAULT_COST*71);
|
|
size(4);
|
|
format %{ "SDIVX $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::sdivx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(divL_reg_imm);
|
|
%}
|
|
|
|
// Integer Remainder
|
|
// Register Remainder
|
|
instruct modI_reg_reg(iRegI dst, iRegIsafe src1, iRegIsafe src2, o7RegP temp, flagsReg ccr ) %{
|
|
match(Set dst (ModI src1 src2));
|
|
effect( KILL ccr, KILL temp);
|
|
|
|
format %{ "SREM $src1,$src2,$dst" %}
|
|
ins_encode( irem_reg(src1, src2, dst, temp) );
|
|
ins_pipe(sdiv_reg_reg);
|
|
%}
|
|
|
|
// Immediate Remainder
|
|
instruct modI_reg_imm13(iRegI dst, iRegIsafe src1, immI13 src2, o7RegP temp, flagsReg ccr ) %{
|
|
match(Set dst (ModI src1 src2));
|
|
effect( KILL ccr, KILL temp);
|
|
|
|
format %{ "SREM $src1,$src2,$dst" %}
|
|
ins_encode( irem_imm(src1, src2, dst, temp) );
|
|
ins_pipe(sdiv_reg_imm);
|
|
%}
|
|
|
|
// Register Long Remainder
|
|
instruct divL_reg_reg_1(iRegL dst, iRegL src1, iRegL src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(4);
|
|
format %{ "SDIVX $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::sdivx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(divL_reg_reg);
|
|
%}
|
|
|
|
// Register Long Division
|
|
instruct divL_reg_imm13_1(iRegL dst, iRegL src1, immL13 src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(4);
|
|
format %{ "SDIVX $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::sdivx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(divL_reg_imm);
|
|
%}
|
|
|
|
instruct mulL_reg_reg_1(iRegL dst, iRegL src1, iRegL src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(4);
|
|
format %{ "MULX $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::mulx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(mulL_reg_reg);
|
|
%}
|
|
|
|
// Immediate Multiplication
|
|
instruct mulL_reg_imm13_1(iRegL dst, iRegL src1, immL13 src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(4);
|
|
format %{ "MULX $src1,$src2,$dst" %}
|
|
opcode(Assembler::mulx_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(mulL_reg_imm);
|
|
%}
|
|
|
|
instruct subL_reg_reg_1(iRegL dst, iRegL src1, iRegL src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(4);
|
|
format %{ "SUB $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::sub_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
instruct subL_reg_reg_2(iRegL dst, iRegL src1, iRegL src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(4);
|
|
format %{ "SUB $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::sub_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Register Long Remainder
|
|
instruct modL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
match(Set dst (ModL src1 src2));
|
|
ins_cost(DEFAULT_COST*(71 + 6 + 1));
|
|
expand %{
|
|
iRegL tmp1;
|
|
iRegL tmp2;
|
|
divL_reg_reg_1(tmp1, src1, src2);
|
|
mulL_reg_reg_1(tmp2, tmp1, src2);
|
|
subL_reg_reg_1(dst, src1, tmp2);
|
|
%}
|
|
%}
|
|
|
|
// Register Long Remainder
|
|
instruct modL_reg_imm13(iRegL dst, iRegL src1, immL13 src2) %{
|
|
match(Set dst (ModL src1 src2));
|
|
ins_cost(DEFAULT_COST*(71 + 6 + 1));
|
|
expand %{
|
|
iRegL tmp1;
|
|
iRegL tmp2;
|
|
divL_reg_imm13_1(tmp1, src1, src2);
|
|
mulL_reg_imm13_1(tmp2, tmp1, src2);
|
|
subL_reg_reg_2 (dst, src1, tmp2);
|
|
%}
|
|
%}
|
|
|
|
// Integer Shift Instructions
|
|
// Register Shift Left
|
|
instruct shlI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
match(Set dst (LShiftI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SLL $src1,$src2,$dst" %}
|
|
opcode(Assembler::sll_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Register Shift Left Immediate
|
|
instruct shlI_reg_imm5(iRegI dst, iRegI src1, immU5 src2) %{
|
|
match(Set dst (LShiftI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SLL $src1,$src2,$dst" %}
|
|
opcode(Assembler::sll_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_imm5_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Register Shift Left
|
|
instruct shlL_reg_reg(iRegL dst, iRegL src1, iRegI src2) %{
|
|
match(Set dst (LShiftL src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SLLX $src1,$src2,$dst" %}
|
|
opcode(Assembler::sllx_op3, Assembler::arith_op);
|
|
ins_encode( form3_sd_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Register Shift Left Immediate
|
|
instruct shlL_reg_imm6(iRegL dst, iRegL src1, immU6 src2) %{
|
|
match(Set dst (LShiftL src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SLLX $src1,$src2,$dst" %}
|
|
opcode(Assembler::sllx_op3, Assembler::arith_op);
|
|
ins_encode( form3_sd_rs1_imm6_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Register Arithmetic Shift Right
|
|
instruct sarI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
match(Set dst (RShiftI src1 src2));
|
|
size(4);
|
|
format %{ "SRA $src1,$src2,$dst" %}
|
|
opcode(Assembler::sra_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Register Arithmetic Shift Right Immediate
|
|
instruct sarI_reg_imm5(iRegI dst, iRegI src1, immU5 src2) %{
|
|
match(Set dst (RShiftI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SRA $src1,$src2,$dst" %}
|
|
opcode(Assembler::sra_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_imm5_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Register Shift Right Arithmatic Long
|
|
instruct sarL_reg_reg(iRegL dst, iRegL src1, iRegI src2) %{
|
|
match(Set dst (RShiftL src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SRAX $src1,$src2,$dst" %}
|
|
opcode(Assembler::srax_op3, Assembler::arith_op);
|
|
ins_encode( form3_sd_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Register Shift Left Immediate
|
|
instruct sarL_reg_imm6(iRegL dst, iRegL src1, immU6 src2) %{
|
|
match(Set dst (RShiftL src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SRAX $src1,$src2,$dst" %}
|
|
opcode(Assembler::srax_op3, Assembler::arith_op);
|
|
ins_encode( form3_sd_rs1_imm6_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Register Shift Right
|
|
instruct shrI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
match(Set dst (URShiftI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SRL $src1,$src2,$dst" %}
|
|
opcode(Assembler::srl_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Register Shift Right Immediate
|
|
instruct shrI_reg_imm5(iRegI dst, iRegI src1, immU5 src2) %{
|
|
match(Set dst (URShiftI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SRL $src1,$src2,$dst" %}
|
|
opcode(Assembler::srl_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_imm5_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Register Shift Right
|
|
instruct shrL_reg_reg(iRegL dst, iRegL src1, iRegI src2) %{
|
|
match(Set dst (URShiftL src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SRLX $src1,$src2,$dst" %}
|
|
opcode(Assembler::srlx_op3, Assembler::arith_op);
|
|
ins_encode( form3_sd_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Register Shift Right Immediate
|
|
instruct shrL_reg_imm6(iRegL dst, iRegL src1, immU6 src2) %{
|
|
match(Set dst (URShiftL src1 src2));
|
|
|
|
size(4);
|
|
format %{ "SRLX $src1,$src2,$dst" %}
|
|
opcode(Assembler::srlx_op3, Assembler::arith_op);
|
|
ins_encode( form3_sd_rs1_imm6_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Register Shift Right Immediate with a CastP2X
|
|
instruct shrP_reg_imm6(iRegL dst, iRegP src1, immU6 src2) %{
|
|
match(Set dst (URShiftL (CastP2X src1) src2));
|
|
size(4);
|
|
format %{ "SRLX $src1,$src2,$dst\t! Cast ptr $src1 to long and shift" %}
|
|
opcode(Assembler::srlx_op3, Assembler::arith_op);
|
|
ins_encode( form3_sd_rs1_imm6_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
|
|
//----------Floating Point Arithmetic Instructions-----------------------------
|
|
|
|
// Add float single precision
|
|
instruct addF_reg_reg(regF dst, regF src1, regF src2) %{
|
|
match(Set dst (AddF src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FADDS $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fadds_opf);
|
|
ins_encode(form3_opf_rs1F_rs2F_rdF(src1, src2, dst));
|
|
ins_pipe(faddF_reg_reg);
|
|
%}
|
|
|
|
// Add float double precision
|
|
instruct addD_reg_reg(regD dst, regD src1, regD src2) %{
|
|
match(Set dst (AddD src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FADDD $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::faddd_opf);
|
|
ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst));
|
|
ins_pipe(faddD_reg_reg);
|
|
%}
|
|
|
|
// Sub float single precision
|
|
instruct subF_reg_reg(regF dst, regF src1, regF src2) %{
|
|
match(Set dst (SubF src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FSUBS $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fsubs_opf);
|
|
ins_encode(form3_opf_rs1F_rs2F_rdF(src1, src2, dst));
|
|
ins_pipe(faddF_reg_reg);
|
|
%}
|
|
|
|
// Sub float double precision
|
|
instruct subD_reg_reg(regD dst, regD src1, regD src2) %{
|
|
match(Set dst (SubD src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FSUBD $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fsubd_opf);
|
|
ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst));
|
|
ins_pipe(faddD_reg_reg);
|
|
%}
|
|
|
|
// Mul float single precision
|
|
instruct mulF_reg_reg(regF dst, regF src1, regF src2) %{
|
|
match(Set dst (MulF src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FMULS $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fmuls_opf);
|
|
ins_encode(form3_opf_rs1F_rs2F_rdF(src1, src2, dst));
|
|
ins_pipe(fmulF_reg_reg);
|
|
%}
|
|
|
|
// Mul float double precision
|
|
instruct mulD_reg_reg(regD dst, regD src1, regD src2) %{
|
|
match(Set dst (MulD src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FMULD $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fmuld_opf);
|
|
ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst));
|
|
ins_pipe(fmulD_reg_reg);
|
|
%}
|
|
|
|
// Div float single precision
|
|
instruct divF_reg_reg(regF dst, regF src1, regF src2) %{
|
|
match(Set dst (DivF src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FDIVS $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fdivs_opf);
|
|
ins_encode(form3_opf_rs1F_rs2F_rdF(src1, src2, dst));
|
|
ins_pipe(fdivF_reg_reg);
|
|
%}
|
|
|
|
// Div float double precision
|
|
instruct divD_reg_reg(regD dst, regD src1, regD src2) %{
|
|
match(Set dst (DivD src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FDIVD $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fdivd_opf);
|
|
ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst));
|
|
ins_pipe(fdivD_reg_reg);
|
|
%}
|
|
|
|
// Absolute float double precision
|
|
instruct absD_reg(regD dst, regD src) %{
|
|
match(Set dst (AbsD src));
|
|
|
|
format %{ "FABSd $src,$dst" %}
|
|
ins_encode(fabsd(dst, src));
|
|
ins_pipe(faddD_reg);
|
|
%}
|
|
|
|
// Absolute float single precision
|
|
instruct absF_reg(regF dst, regF src) %{
|
|
match(Set dst (AbsF src));
|
|
|
|
format %{ "FABSs $src,$dst" %}
|
|
ins_encode(fabss(dst, src));
|
|
ins_pipe(faddF_reg);
|
|
%}
|
|
|
|
instruct negF_reg(regF dst, regF src) %{
|
|
match(Set dst (NegF src));
|
|
|
|
size(4);
|
|
format %{ "FNEGs $src,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fnegs_opf);
|
|
ins_encode(form3_opf_rs2F_rdF(src, dst));
|
|
ins_pipe(faddF_reg);
|
|
%}
|
|
|
|
instruct negD_reg(regD dst, regD src) %{
|
|
match(Set dst (NegD src));
|
|
|
|
format %{ "FNEGd $src,$dst" %}
|
|
ins_encode(fnegd(dst, src));
|
|
ins_pipe(faddD_reg);
|
|
%}
|
|
|
|
// Sqrt float double precision
|
|
instruct sqrtF_reg_reg(regF dst, regF src) %{
|
|
match(Set dst (ConvD2F (SqrtD (ConvF2D src))));
|
|
|
|
size(4);
|
|
format %{ "FSQRTS $src,$dst" %}
|
|
ins_encode(fsqrts(dst, src));
|
|
ins_pipe(fdivF_reg_reg);
|
|
%}
|
|
|
|
// Sqrt float double precision
|
|
instruct sqrtD_reg_reg(regD dst, regD src) %{
|
|
match(Set dst (SqrtD src));
|
|
|
|
size(4);
|
|
format %{ "FSQRTD $src,$dst" %}
|
|
ins_encode(fsqrtd(dst, src));
|
|
ins_pipe(fdivD_reg_reg);
|
|
%}
|
|
|
|
// Single/Double precision fused floating-point multiply-add (d = a * b + c).
|
|
instruct fmaF_regx4(regF dst, regF a, regF b, regF c) %{
|
|
predicate(UseFMA);
|
|
match(Set dst (FmaF c (Binary a b)));
|
|
format %{ "fmadds $a,$b,$c,$dst\t# $dst = $a * $b + $c" %}
|
|
ins_encode(fmadds(dst, a, b, c));
|
|
ins_pipe(fmaF_regx4);
|
|
%}
|
|
|
|
instruct fmaD_regx4(regD dst, regD a, regD b, regD c) %{
|
|
predicate(UseFMA);
|
|
match(Set dst (FmaD c (Binary a b)));
|
|
format %{ "fmaddd $a,$b,$c,$dst\t# $dst = $a * $b + $c" %}
|
|
ins_encode(fmaddd(dst, a, b, c));
|
|
ins_pipe(fmaD_regx4);
|
|
%}
|
|
|
|
// Additional patterns matching complement versions that we can map directly to
|
|
// variants of the fused multiply-add instructions.
|
|
|
|
// Single/Double precision fused floating-point multiply-sub (d = a * b - c)
|
|
instruct fmsubF_regx4(regF dst, regF a, regF b, regF c) %{
|
|
predicate(UseFMA);
|
|
match(Set dst (FmaF (NegF c) (Binary a b)));
|
|
format %{ "fmsubs $a,$b,$c,$dst\t# $dst = $a * $b - $c" %}
|
|
ins_encode(fmsubs(dst, a, b, c));
|
|
ins_pipe(fmaF_regx4);
|
|
%}
|
|
|
|
instruct fmsubD_regx4(regD dst, regD a, regD b, regD c) %{
|
|
predicate(UseFMA);
|
|
match(Set dst (FmaD (NegD c) (Binary a b)));
|
|
format %{ "fmsubd $a,$b,$c,$dst\t# $dst = $a * $b - $c" %}
|
|
ins_encode(fmsubd(dst, a, b, c));
|
|
ins_pipe(fmaD_regx4);
|
|
%}
|
|
|
|
// Single/Double precision fused floating-point neg. multiply-add,
|
|
// d = -1 * a * b - c = -(a * b + c)
|
|
instruct fnmaddF_regx4(regF dst, regF a, regF b, regF c) %{
|
|
predicate(UseFMA);
|
|
match(Set dst (FmaF (NegF c) (Binary (NegF a) b)));
|
|
match(Set dst (FmaF (NegF c) (Binary a (NegF b))));
|
|
format %{ "fnmadds $a,$b,$c,$dst\t# $dst = -($a * $b + $c)" %}
|
|
ins_encode(fnmadds(dst, a, b, c));
|
|
ins_pipe(fmaF_regx4);
|
|
%}
|
|
|
|
instruct fnmaddD_regx4(regD dst, regD a, regD b, regD c) %{
|
|
predicate(UseFMA);
|
|
match(Set dst (FmaD (NegD c) (Binary (NegD a) b)));
|
|
match(Set dst (FmaD (NegD c) (Binary a (NegD b))));
|
|
format %{ "fnmaddd $a,$b,$c,$dst\t# $dst = -($a * $b + $c)" %}
|
|
ins_encode(fnmaddd(dst, a, b, c));
|
|
ins_pipe(fmaD_regx4);
|
|
%}
|
|
|
|
// Single/Double precision fused floating-point neg. multiply-sub,
|
|
// d = -1 * a * b + c = -(a * b - c)
|
|
instruct fnmsubF_regx4(regF dst, regF a, regF b, regF c) %{
|
|
predicate(UseFMA);
|
|
match(Set dst (FmaF c (Binary (NegF a) b)));
|
|
match(Set dst (FmaF c (Binary a (NegF b))));
|
|
format %{ "fnmsubs $a,$b,$c,$dst\t# $dst = -($a * $b - $c)" %}
|
|
ins_encode(fnmsubs(dst, a, b, c));
|
|
ins_pipe(fmaF_regx4);
|
|
%}
|
|
|
|
instruct fnmsubD_regx4(regD dst, regD a, regD b, regD c) %{
|
|
predicate(UseFMA);
|
|
match(Set dst (FmaD c (Binary (NegD a) b)));
|
|
match(Set dst (FmaD c (Binary a (NegD b))));
|
|
format %{ "fnmsubd $a,$b,$c,$dst\t# $dst = -($a * $b - $c)" %}
|
|
ins_encode(fnmsubd(dst, a, b, c));
|
|
ins_pipe(fmaD_regx4);
|
|
%}
|
|
|
|
//----------Logical Instructions-----------------------------------------------
|
|
// And Instructions
|
|
// Register And
|
|
instruct andI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
match(Set dst (AndI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "AND $src1,$src2,$dst" %}
|
|
opcode(Assembler::and_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Immediate And
|
|
instruct andI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{
|
|
match(Set dst (AndI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "AND $src1,$src2,$dst" %}
|
|
opcode(Assembler::and_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Register And Long
|
|
instruct andL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
match(Set dst (AndL src1 src2));
|
|
|
|
ins_cost(DEFAULT_COST);
|
|
size(4);
|
|
format %{ "AND $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::and_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
instruct andL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{
|
|
match(Set dst (AndL src1 con));
|
|
|
|
ins_cost(DEFAULT_COST);
|
|
size(4);
|
|
format %{ "AND $src1,$con,$dst\t! long" %}
|
|
opcode(Assembler::and_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, con, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Or Instructions
|
|
// Register Or
|
|
instruct orI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
match(Set dst (OrI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "OR $src1,$src2,$dst" %}
|
|
opcode(Assembler::or_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Immediate Or
|
|
instruct orI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{
|
|
match(Set dst (OrI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "OR $src1,$src2,$dst" %}
|
|
opcode(Assembler::or_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Register Or Long
|
|
instruct orL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
match(Set dst (OrL src1 src2));
|
|
|
|
ins_cost(DEFAULT_COST);
|
|
size(4);
|
|
format %{ "OR $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::or_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
instruct orL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{
|
|
match(Set dst (OrL src1 con));
|
|
ins_cost(DEFAULT_COST*2);
|
|
|
|
ins_cost(DEFAULT_COST);
|
|
size(4);
|
|
format %{ "OR $src1,$con,$dst\t! long" %}
|
|
opcode(Assembler::or_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, con, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
instruct orL_reg_castP2X(iRegL dst, iRegL src1, sp_ptr_RegP src2) %{
|
|
match(Set dst (OrL src1 (CastP2X src2)));
|
|
|
|
ins_cost(DEFAULT_COST);
|
|
size(4);
|
|
format %{ "OR $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::or_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Xor Instructions
|
|
// Register Xor
|
|
instruct xorI_reg_reg(iRegI dst, iRegI src1, iRegI src2) %{
|
|
match(Set dst (XorI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "XOR $src1,$src2,$dst" %}
|
|
opcode(Assembler::xor_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Immediate Xor
|
|
instruct xorI_reg_imm13(iRegI dst, iRegI src1, immI13 src2) %{
|
|
match(Set dst (XorI src1 src2));
|
|
|
|
size(4);
|
|
format %{ "XOR $src1,$src2,$dst" %}
|
|
opcode(Assembler::xor_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
// Register Xor Long
|
|
instruct xorL_reg_reg(iRegL dst, iRegL src1, iRegL src2) %{
|
|
match(Set dst (XorL src1 src2));
|
|
|
|
ins_cost(DEFAULT_COST);
|
|
size(4);
|
|
format %{ "XOR $src1,$src2,$dst\t! long" %}
|
|
opcode(Assembler::xor_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src1, src2, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
instruct xorL_reg_imm13(iRegL dst, iRegL src1, immL13 con) %{
|
|
match(Set dst (XorL src1 con));
|
|
|
|
ins_cost(DEFAULT_COST);
|
|
size(4);
|
|
format %{ "XOR $src1,$con,$dst\t! long" %}
|
|
opcode(Assembler::xor_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( src1, con, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
//----------Convert to Boolean-------------------------------------------------
|
|
// Nice hack for 32-bit tests but doesn't work for
|
|
// 64-bit pointers.
|
|
instruct convI2B( iRegI dst, iRegI src, flagsReg ccr ) %{
|
|
match(Set dst (Conv2B src));
|
|
effect( KILL ccr );
|
|
ins_cost(DEFAULT_COST*2);
|
|
format %{ "CMP R_G0,$src\n\t"
|
|
"ADDX R_G0,0,$dst" %}
|
|
ins_encode( enc_to_bool( src, dst ) );
|
|
ins_pipe(ialu_reg_ialu);
|
|
%}
|
|
|
|
instruct convP2B( iRegI dst, iRegP src ) %{
|
|
match(Set dst (Conv2B src));
|
|
ins_cost(DEFAULT_COST*2);
|
|
format %{ "MOV $src,$dst\n\t"
|
|
"MOVRNZ $src,1,$dst" %}
|
|
ins_encode( form3_g0_rs2_rd_move( src, dst ), enc_convP2B( dst, src ) );
|
|
ins_pipe(ialu_clr_and_mover);
|
|
%}
|
|
|
|
instruct cmpLTMask0( iRegI dst, iRegI src, immI0 zero, flagsReg ccr ) %{
|
|
match(Set dst (CmpLTMask src zero));
|
|
effect(KILL ccr);
|
|
size(4);
|
|
format %{ "SRA $src,#31,$dst\t# cmpLTMask0" %}
|
|
ins_encode %{
|
|
__ sra($src$$Register, 31, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
instruct cmpLTMask_reg_reg( iRegI dst, iRegI p, iRegI q, flagsReg ccr ) %{
|
|
match(Set dst (CmpLTMask p q));
|
|
effect( KILL ccr );
|
|
ins_cost(DEFAULT_COST*4);
|
|
format %{ "CMP $p,$q\n\t"
|
|
"MOV #0,$dst\n\t"
|
|
"BLT,a .+8\n\t"
|
|
"MOV #-1,$dst" %}
|
|
ins_encode( enc_ltmask(p,q,dst) );
|
|
ins_pipe(ialu_reg_reg_ialu);
|
|
%}
|
|
|
|
instruct cadd_cmpLTMask( iRegI p, iRegI q, iRegI y, iRegI tmp, flagsReg ccr ) %{
|
|
match(Set p (AddI (AndI (CmpLTMask p q) y) (SubI p q)));
|
|
effect(KILL ccr, TEMP tmp);
|
|
ins_cost(DEFAULT_COST*3);
|
|
|
|
format %{ "SUBcc $p,$q,$p\t! p' = p-q\n\t"
|
|
"ADD $p,$y,$tmp\t! g3=p-q+y\n\t"
|
|
"MOVlt $tmp,$p\t! p' < 0 ? p'+y : p'" %}
|
|
ins_encode(enc_cadd_cmpLTMask(p, q, y, tmp));
|
|
ins_pipe(cadd_cmpltmask);
|
|
%}
|
|
|
|
instruct and_cmpLTMask(iRegI p, iRegI q, iRegI y, flagsReg ccr) %{
|
|
match(Set p (AndI (CmpLTMask p q) y));
|
|
effect(KILL ccr);
|
|
ins_cost(DEFAULT_COST*3);
|
|
|
|
format %{ "CMP $p,$q\n\t"
|
|
"MOV $y,$p\n\t"
|
|
"MOVge G0,$p" %}
|
|
ins_encode %{
|
|
__ cmp($p$$Register, $q$$Register);
|
|
__ mov($y$$Register, $p$$Register);
|
|
__ movcc(Assembler::greaterEqual, false, Assembler::icc, G0, $p$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg_reg_ialu);
|
|
%}
|
|
|
|
//-----------------------------------------------------------------
|
|
// Direct raw moves between float and general registers using VIS3.
|
|
|
|
// ins_pipe(faddF_reg);
|
|
instruct MoveF2I_reg_reg(iRegI dst, regF src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (MoveF2I src));
|
|
|
|
format %{ "MOVSTOUW $src,$dst\t! MoveF2I" %}
|
|
ins_encode %{
|
|
__ movstouw($src$$FloatRegister, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
instruct MoveI2F_reg_reg(regF dst, iRegI src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (MoveI2F src));
|
|
|
|
format %{ "MOVWTOS $src,$dst\t! MoveI2F" %}
|
|
ins_encode %{
|
|
__ movwtos($src$$Register, $dst$$FloatRegister);
|
|
%}
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
instruct MoveD2L_reg_reg(iRegL dst, regD src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (MoveD2L src));
|
|
|
|
format %{ "MOVDTOX $src,$dst\t! MoveD2L" %}
|
|
ins_encode %{
|
|
__ movdtox(as_DoubleFloatRegister($src$$reg), $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
instruct MoveL2D_reg_reg(regD dst, iRegL src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (MoveL2D src));
|
|
|
|
format %{ "MOVXTOD $src,$dst\t! MoveL2D" %}
|
|
ins_encode %{
|
|
__ movxtod($src$$Register, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
|
|
// Raw moves between float and general registers using stack.
|
|
|
|
instruct MoveF2I_stack_reg(iRegI dst, stackSlotF src) %{
|
|
match(Set dst (MoveF2I src));
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDUW $src,$dst\t! MoveF2I" %}
|
|
opcode(Assembler::lduw_op3);
|
|
ins_encode(simple_form3_mem_reg( src, dst ) );
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
instruct MoveI2F_stack_reg(regF dst, stackSlotI src) %{
|
|
match(Set dst (MoveI2F src));
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDF $src,$dst\t! MoveI2F" %}
|
|
opcode(Assembler::ldf_op3);
|
|
ins_encode(simple_form3_mem_reg(src, dst));
|
|
ins_pipe(floadF_stk);
|
|
%}
|
|
|
|
instruct MoveD2L_stack_reg(iRegL dst, stackSlotD src) %{
|
|
match(Set dst (MoveD2L src));
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDX $src,$dst\t! MoveD2L" %}
|
|
opcode(Assembler::ldx_op3);
|
|
ins_encode(simple_form3_mem_reg( src, dst ) );
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
instruct MoveL2D_stack_reg(regD dst, stackSlotL src) %{
|
|
match(Set dst (MoveL2D src));
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "LDDF $src,$dst\t! MoveL2D" %}
|
|
opcode(Assembler::lddf_op3);
|
|
ins_encode(simple_form3_mem_reg(src, dst));
|
|
ins_pipe(floadD_stk);
|
|
%}
|
|
|
|
instruct MoveF2I_reg_stack(stackSlotI dst, regF src) %{
|
|
match(Set dst (MoveF2I src));
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STF $src,$dst\t! MoveF2I" %}
|
|
opcode(Assembler::stf_op3);
|
|
ins_encode(simple_form3_mem_reg(dst, src));
|
|
ins_pipe(fstoreF_stk_reg);
|
|
%}
|
|
|
|
instruct MoveI2F_reg_stack(stackSlotF dst, iRegI src) %{
|
|
match(Set dst (MoveI2F src));
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STW $src,$dst\t! MoveI2F" %}
|
|
opcode(Assembler::stw_op3);
|
|
ins_encode(simple_form3_mem_reg( dst, src ) );
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
instruct MoveD2L_reg_stack(stackSlotL dst, regD src) %{
|
|
match(Set dst (MoveD2L src));
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STDF $src,$dst\t! MoveD2L" %}
|
|
opcode(Assembler::stdf_op3);
|
|
ins_encode(simple_form3_mem_reg(dst, src));
|
|
ins_pipe(fstoreD_stk_reg);
|
|
%}
|
|
|
|
instruct MoveL2D_reg_stack(stackSlotD dst, iRegL src) %{
|
|
match(Set dst (MoveL2D src));
|
|
effect(DEF dst, USE src);
|
|
ins_cost(MEMORY_REF_COST);
|
|
|
|
format %{ "STX $src,$dst\t! MoveL2D" %}
|
|
opcode(Assembler::stx_op3);
|
|
ins_encode(simple_form3_mem_reg( dst, src ) );
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
|
|
//----------Arithmetic Conversion Instructions---------------------------------
|
|
// The conversions operations are all Alpha sorted. Please keep it that way!
|
|
|
|
instruct convD2F_reg(regF dst, regD src) %{
|
|
match(Set dst (ConvD2F src));
|
|
size(4);
|
|
format %{ "FDTOS $src,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fdtos_opf);
|
|
ins_encode(form3_opf_rs2D_rdF(src, dst));
|
|
ins_pipe(fcvtD2F);
|
|
%}
|
|
|
|
|
|
// Convert a double to an int in a float register.
|
|
// If the double is a NAN, stuff a zero in instead.
|
|
instruct convD2I_helper(regF dst, regD src, flagsRegF0 fcc0) %{
|
|
effect(DEF dst, USE src, KILL fcc0);
|
|
format %{ "FCMPd fcc0,$src,$src\t! check for NAN\n\t"
|
|
"FBO,pt fcc0,skip\t! branch on ordered, predict taken\n\t"
|
|
"FDTOI $src,$dst\t! convert in delay slot\n\t"
|
|
"FITOS $dst,$dst\t! change NaN/max-int to valid float\n\t"
|
|
"FSUBs $dst,$dst,$dst\t! cleared only if nan\n"
|
|
"skip:" %}
|
|
ins_encode(form_d2i_helper(src,dst));
|
|
ins_pipe(fcvtD2I);
|
|
%}
|
|
|
|
instruct convD2I_stk(stackSlotI dst, regD src) %{
|
|
match(Set dst (ConvD2I src));
|
|
ins_cost(DEFAULT_COST*2 + MEMORY_REF_COST*2 + BRANCH_COST);
|
|
expand %{
|
|
regF tmp;
|
|
convD2I_helper(tmp, src);
|
|
regF_to_stkI(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convD2I_reg(iRegI dst, regD src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (ConvD2I src));
|
|
ins_cost(DEFAULT_COST*2 + BRANCH_COST);
|
|
expand %{
|
|
regF tmp;
|
|
convD2I_helper(tmp, src);
|
|
MoveF2I_reg_reg(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
|
|
// Convert a double to a long in a double register.
|
|
// If the double is a NAN, stuff a zero in instead.
|
|
instruct convD2L_helper(regD dst, regD src, flagsRegF0 fcc0) %{
|
|
effect(DEF dst, USE src, KILL fcc0);
|
|
format %{ "FCMPd fcc0,$src,$src\t! check for NAN\n\t"
|
|
"FBO,pt fcc0,skip\t! branch on ordered, predict taken\n\t"
|
|
"FDTOX $src,$dst\t! convert in delay slot\n\t"
|
|
"FXTOD $dst,$dst\t! change NaN/max-long to valid double\n\t"
|
|
"FSUBd $dst,$dst,$dst\t! cleared only if nan\n"
|
|
"skip:" %}
|
|
ins_encode(form_d2l_helper(src,dst));
|
|
ins_pipe(fcvtD2L);
|
|
%}
|
|
|
|
instruct convD2L_stk(stackSlotL dst, regD src) %{
|
|
match(Set dst (ConvD2L src));
|
|
ins_cost(DEFAULT_COST*2 + MEMORY_REF_COST*2 + BRANCH_COST);
|
|
expand %{
|
|
regD tmp;
|
|
convD2L_helper(tmp, src);
|
|
regD_to_stkL(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convD2L_reg(iRegL dst, regD src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (ConvD2L src));
|
|
ins_cost(DEFAULT_COST*2 + BRANCH_COST);
|
|
expand %{
|
|
regD tmp;
|
|
convD2L_helper(tmp, src);
|
|
MoveD2L_reg_reg(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
|
|
instruct convF2D_reg(regD dst, regF src) %{
|
|
match(Set dst (ConvF2D src));
|
|
format %{ "FSTOD $src,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fstod_opf);
|
|
ins_encode(form3_opf_rs2F_rdD(src, dst));
|
|
ins_pipe(fcvtF2D);
|
|
%}
|
|
|
|
|
|
// Convert a float to an int in a float register.
|
|
// If the float is a NAN, stuff a zero in instead.
|
|
instruct convF2I_helper(regF dst, regF src, flagsRegF0 fcc0) %{
|
|
effect(DEF dst, USE src, KILL fcc0);
|
|
format %{ "FCMPs fcc0,$src,$src\t! check for NAN\n\t"
|
|
"FBO,pt fcc0,skip\t! branch on ordered, predict taken\n\t"
|
|
"FSTOI $src,$dst\t! convert in delay slot\n\t"
|
|
"FITOS $dst,$dst\t! change NaN/max-int to valid float\n\t"
|
|
"FSUBs $dst,$dst,$dst\t! cleared only if nan\n"
|
|
"skip:" %}
|
|
ins_encode(form_f2i_helper(src,dst));
|
|
ins_pipe(fcvtF2I);
|
|
%}
|
|
|
|
instruct convF2I_stk(stackSlotI dst, regF src) %{
|
|
match(Set dst (ConvF2I src));
|
|
ins_cost(DEFAULT_COST*2 + MEMORY_REF_COST*2 + BRANCH_COST);
|
|
expand %{
|
|
regF tmp;
|
|
convF2I_helper(tmp, src);
|
|
regF_to_stkI(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convF2I_reg(iRegI dst, regF src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (ConvF2I src));
|
|
ins_cost(DEFAULT_COST*2 + BRANCH_COST);
|
|
expand %{
|
|
regF tmp;
|
|
convF2I_helper(tmp, src);
|
|
MoveF2I_reg_reg(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
|
|
// Convert a float to a long in a float register.
|
|
// If the float is a NAN, stuff a zero in instead.
|
|
instruct convF2L_helper(regD dst, regF src, flagsRegF0 fcc0) %{
|
|
effect(DEF dst, USE src, KILL fcc0);
|
|
format %{ "FCMPs fcc0,$src,$src\t! check for NAN\n\t"
|
|
"FBO,pt fcc0,skip\t! branch on ordered, predict taken\n\t"
|
|
"FSTOX $src,$dst\t! convert in delay slot\n\t"
|
|
"FXTOD $dst,$dst\t! change NaN/max-long to valid double\n\t"
|
|
"FSUBd $dst,$dst,$dst\t! cleared only if nan\n"
|
|
"skip:" %}
|
|
ins_encode(form_f2l_helper(src,dst));
|
|
ins_pipe(fcvtF2L);
|
|
%}
|
|
|
|
instruct convF2L_stk(stackSlotL dst, regF src) %{
|
|
match(Set dst (ConvF2L src));
|
|
ins_cost(DEFAULT_COST*2 + MEMORY_REF_COST*2 + BRANCH_COST);
|
|
expand %{
|
|
regD tmp;
|
|
convF2L_helper(tmp, src);
|
|
regD_to_stkL(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convF2L_reg(iRegL dst, regF src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (ConvF2L src));
|
|
ins_cost(DEFAULT_COST*2 + BRANCH_COST);
|
|
expand %{
|
|
regD tmp;
|
|
convF2L_helper(tmp, src);
|
|
MoveD2L_reg_reg(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
|
|
instruct convI2D_helper(regD dst, regF tmp) %{
|
|
effect(USE tmp, DEF dst);
|
|
format %{ "FITOD $tmp,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fitod_opf);
|
|
ins_encode(form3_opf_rs2F_rdD(tmp, dst));
|
|
ins_pipe(fcvtI2D);
|
|
%}
|
|
|
|
instruct convI2D_stk(stackSlotI src, regD dst) %{
|
|
match(Set dst (ConvI2D src));
|
|
ins_cost(DEFAULT_COST + MEMORY_REF_COST);
|
|
expand %{
|
|
regF tmp;
|
|
stkI_to_regF(tmp, src);
|
|
convI2D_helper(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convI2D_reg(regD_low dst, iRegI src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (ConvI2D src));
|
|
expand %{
|
|
regF tmp;
|
|
MoveI2F_reg_reg(tmp, src);
|
|
convI2D_helper(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convI2D_mem(regD_low dst, memory mem) %{
|
|
match(Set dst (ConvI2D (LoadI mem)));
|
|
ins_cost(DEFAULT_COST + MEMORY_REF_COST);
|
|
format %{ "LDF $mem,$dst\n\t"
|
|
"FITOD $dst,$dst" %}
|
|
opcode(Assembler::ldf_op3, Assembler::fitod_opf);
|
|
ins_encode(simple_form3_mem_reg( mem, dst ), form3_convI2F(dst, dst));
|
|
ins_pipe(floadF_mem);
|
|
%}
|
|
|
|
|
|
instruct convI2F_helper(regF dst, regF tmp) %{
|
|
effect(DEF dst, USE tmp);
|
|
format %{ "FITOS $tmp,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fitos_opf);
|
|
ins_encode(form3_opf_rs2F_rdF(tmp, dst));
|
|
ins_pipe(fcvtI2F);
|
|
%}
|
|
|
|
instruct convI2F_stk(regF dst, stackSlotI src) %{
|
|
match(Set dst (ConvI2F src));
|
|
ins_cost(DEFAULT_COST + MEMORY_REF_COST);
|
|
expand %{
|
|
regF tmp;
|
|
stkI_to_regF(tmp,src);
|
|
convI2F_helper(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convI2F_reg(regF dst, iRegI src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (ConvI2F src));
|
|
ins_cost(DEFAULT_COST);
|
|
expand %{
|
|
regF tmp;
|
|
MoveI2F_reg_reg(tmp, src);
|
|
convI2F_helper(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convI2F_mem( regF dst, memory mem ) %{
|
|
match(Set dst (ConvI2F (LoadI mem)));
|
|
ins_cost(DEFAULT_COST + MEMORY_REF_COST);
|
|
format %{ "LDF $mem,$dst\n\t"
|
|
"FITOS $dst,$dst" %}
|
|
opcode(Assembler::ldf_op3, Assembler::fitos_opf);
|
|
ins_encode(simple_form3_mem_reg( mem, dst ), form3_convI2F(dst, dst));
|
|
ins_pipe(floadF_mem);
|
|
%}
|
|
|
|
|
|
instruct convI2L_reg(iRegL dst, iRegI src) %{
|
|
match(Set dst (ConvI2L src));
|
|
size(4);
|
|
format %{ "SRA $src,0,$dst\t! int->long" %}
|
|
opcode(Assembler::sra_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src, R_G0, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Zero-extend convert int to long
|
|
instruct convI2L_reg_zex(iRegL dst, iRegI src, immL_32bits mask ) %{
|
|
match(Set dst (AndL (ConvI2L src) mask) );
|
|
size(4);
|
|
format %{ "SRL $src,0,$dst\t! zero-extend int to long" %}
|
|
opcode(Assembler::srl_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src, R_G0, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Zero-extend long
|
|
instruct zerox_long(iRegL dst, iRegL src, immL_32bits mask ) %{
|
|
match(Set dst (AndL src mask) );
|
|
size(4);
|
|
format %{ "SRL $src,0,$dst\t! zero-extend long" %}
|
|
opcode(Assembler::srl_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( src, R_G0, dst ) );
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
|
|
//-----------
|
|
// Long to Double conversion using V8 opcodes.
|
|
// Still useful because cheetah traps and becomes
|
|
// amazingly slow for some common numbers.
|
|
|
|
// Magic constant, 0x43300000
|
|
instruct loadConI_x43300000(iRegI dst) %{
|
|
effect(DEF dst);
|
|
size(4);
|
|
format %{ "SETHI HI(0x43300000),$dst\t! 2^52" %}
|
|
ins_encode(SetHi22(0x43300000, dst));
|
|
ins_pipe(ialu_none);
|
|
%}
|
|
|
|
// Magic constant, 0x41f00000
|
|
instruct loadConI_x41f00000(iRegI dst) %{
|
|
effect(DEF dst);
|
|
size(4);
|
|
format %{ "SETHI HI(0x41f00000),$dst\t! 2^32" %}
|
|
ins_encode(SetHi22(0x41f00000, dst));
|
|
ins_pipe(ialu_none);
|
|
%}
|
|
|
|
// Construct a double from two float halves
|
|
instruct regDHi_regDLo_to_regD(regD_low dst, regD_low src1, regD_low src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(8);
|
|
format %{ "FMOVS $src1.hi,$dst.hi\n\t"
|
|
"FMOVS $src2.lo,$dst.lo" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fmovs_opf);
|
|
ins_encode(form3_opf_rs2D_hi_rdD_hi(src1, dst), form3_opf_rs2D_lo_rdD_lo(src2, dst));
|
|
ins_pipe(faddD_reg_reg);
|
|
%}
|
|
|
|
// Convert integer in high half of a double register (in the lower half of
|
|
// the double register file) to double
|
|
instruct convI2D_regDHi_regD(regD dst, regD_low src) %{
|
|
effect(DEF dst, USE src);
|
|
size(4);
|
|
format %{ "FITOD $src,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fitod_opf);
|
|
ins_encode(form3_opf_rs2D_rdD(src, dst));
|
|
ins_pipe(fcvtLHi2D);
|
|
%}
|
|
|
|
// Add float double precision
|
|
instruct addD_regD_regD(regD dst, regD src1, regD src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(4);
|
|
format %{ "FADDD $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::faddd_opf);
|
|
ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst));
|
|
ins_pipe(faddD_reg_reg);
|
|
%}
|
|
|
|
// Sub float double precision
|
|
instruct subD_regD_regD(regD dst, regD src1, regD src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(4);
|
|
format %{ "FSUBD $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fsubd_opf);
|
|
ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst));
|
|
ins_pipe(faddD_reg_reg);
|
|
%}
|
|
|
|
// Mul float double precision
|
|
instruct mulD_regD_regD(regD dst, regD src1, regD src2) %{
|
|
effect(DEF dst, USE src1, USE src2);
|
|
size(4);
|
|
format %{ "FMULD $src1,$src2,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fmuld_opf);
|
|
ins_encode(form3_opf_rs1D_rs2D_rdD(src1, src2, dst));
|
|
ins_pipe(fmulD_reg_reg);
|
|
%}
|
|
|
|
// Long to Double conversion using fast fxtof
|
|
instruct convL2D_helper(regD dst, regD tmp) %{
|
|
effect(DEF dst, USE tmp);
|
|
size(4);
|
|
format %{ "FXTOD $tmp,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fxtod_opf);
|
|
ins_encode(form3_opf_rs2D_rdD(tmp, dst));
|
|
ins_pipe(fcvtL2D);
|
|
%}
|
|
|
|
instruct convL2D_stk_fast_fxtof(regD dst, stackSlotL src) %{
|
|
match(Set dst (ConvL2D src));
|
|
ins_cost(DEFAULT_COST + 3 * MEMORY_REF_COST);
|
|
expand %{
|
|
regD tmp;
|
|
stkL_to_regD(tmp, src);
|
|
convL2D_helper(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convL2D_reg(regD dst, iRegL src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (ConvL2D src));
|
|
expand %{
|
|
regD tmp;
|
|
MoveL2D_reg_reg(tmp, src);
|
|
convL2D_helper(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
// Long to Float conversion using fast fxtof
|
|
instruct convL2F_helper(regF dst, regD tmp) %{
|
|
effect(DEF dst, USE tmp);
|
|
size(4);
|
|
format %{ "FXTOS $tmp,$dst" %}
|
|
opcode(Assembler::fpop1_op3, Assembler::arith_op, Assembler::fxtos_opf);
|
|
ins_encode(form3_opf_rs2D_rdF(tmp, dst));
|
|
ins_pipe(fcvtL2F);
|
|
%}
|
|
|
|
instruct convL2F_stk_fast_fxtof(regF dst, stackSlotL src) %{
|
|
match(Set dst (ConvL2F src));
|
|
ins_cost(DEFAULT_COST + MEMORY_REF_COST);
|
|
expand %{
|
|
regD tmp;
|
|
stkL_to_regD(tmp, src);
|
|
convL2F_helper(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
instruct convL2F_reg(regF dst, iRegL src) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dst (ConvL2F src));
|
|
ins_cost(DEFAULT_COST);
|
|
expand %{
|
|
regD tmp;
|
|
MoveL2D_reg_reg(tmp, src);
|
|
convL2F_helper(dst, tmp);
|
|
%}
|
|
%}
|
|
|
|
//-----------
|
|
|
|
instruct convL2I_reg(iRegI dst, iRegL src) %{
|
|
match(Set dst (ConvL2I src));
|
|
size(4);
|
|
format %{ "SRA $src,R_G0,$dst\t! long->int" %}
|
|
ins_encode( form3_rs1_rd_signextend_lo1( src, dst ) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Register Shift Right Immediate
|
|
instruct shrL_reg_imm6_L2I(iRegI dst, iRegL src, immI_32_63 cnt) %{
|
|
match(Set dst (ConvL2I (RShiftL src cnt)));
|
|
|
|
size(4);
|
|
format %{ "SRAX $src,$cnt,$dst" %}
|
|
opcode(Assembler::srax_op3, Assembler::arith_op);
|
|
ins_encode( form3_sd_rs1_imm6_rd( src, cnt, dst ) );
|
|
ins_pipe(ialu_reg_imm);
|
|
%}
|
|
|
|
//----------Control Flow Instructions------------------------------------------
|
|
// Compare Instructions
|
|
// Compare Integers
|
|
instruct compI_iReg(flagsReg icc, iRegI op1, iRegI op2) %{
|
|
match(Set icc (CmpI op1 op2));
|
|
effect( DEF icc, USE op1, USE op2 );
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_reg);
|
|
%}
|
|
|
|
instruct compU_iReg(flagsRegU icc, iRegI op1, iRegI op2) %{
|
|
match(Set icc (CmpU op1 op2));
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2\t! unsigned" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_reg);
|
|
%}
|
|
|
|
instruct compUL_iReg(flagsRegUL xcc, iRegL op1, iRegL op2) %{
|
|
match(Set xcc (CmpUL op1 op2));
|
|
effect(DEF xcc, USE op1, USE op2);
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2\t! unsigned long" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode(form3_rs1_rs2_rd(op1, op2, R_G0));
|
|
ins_pipe(ialu_cconly_reg_reg);
|
|
%}
|
|
|
|
instruct compI_iReg_imm13(flagsReg icc, iRegI op1, immI13 op2) %{
|
|
match(Set icc (CmpI op1 op2));
|
|
effect( DEF icc, USE op1 );
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_imm);
|
|
%}
|
|
|
|
instruct testI_reg_reg( flagsReg icc, iRegI op1, iRegI op2, immI0 zero ) %{
|
|
match(Set icc (CmpI (AndI op1 op2) zero));
|
|
|
|
size(4);
|
|
format %{ "BTST $op2,$op1" %}
|
|
opcode(Assembler::andcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_reg_zero);
|
|
%}
|
|
|
|
instruct testI_reg_imm( flagsReg icc, iRegI op1, immI13 op2, immI0 zero ) %{
|
|
match(Set icc (CmpI (AndI op1 op2) zero));
|
|
|
|
size(4);
|
|
format %{ "BTST $op2,$op1" %}
|
|
opcode(Assembler::andcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_imm_zero);
|
|
%}
|
|
|
|
instruct compL_reg_reg(flagsRegL xcc, iRegL op1, iRegL op2 ) %{
|
|
match(Set xcc (CmpL op1 op2));
|
|
effect( DEF xcc, USE op1, USE op2 );
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2\t\t! long" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_reg);
|
|
%}
|
|
|
|
instruct compL_reg_con(flagsRegL xcc, iRegL op1, immL13 con) %{
|
|
match(Set xcc (CmpL op1 con));
|
|
effect( DEF xcc, USE op1, USE con );
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$con\t\t! long" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( op1, con, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_reg);
|
|
%}
|
|
|
|
instruct testL_reg_reg(flagsRegL xcc, iRegL op1, iRegL op2, immL0 zero) %{
|
|
match(Set xcc (CmpL (AndL op1 op2) zero));
|
|
effect( DEF xcc, USE op1, USE op2 );
|
|
|
|
size(4);
|
|
format %{ "BTST $op1,$op2\t\t! long" %}
|
|
opcode(Assembler::andcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_reg);
|
|
%}
|
|
|
|
// useful for checking the alignment of a pointer:
|
|
instruct testL_reg_con(flagsRegL xcc, iRegL op1, immL13 con, immL0 zero) %{
|
|
match(Set xcc (CmpL (AndL op1 con) zero));
|
|
effect( DEF xcc, USE op1, USE con );
|
|
|
|
size(4);
|
|
format %{ "BTST $op1,$con\t\t! long" %}
|
|
opcode(Assembler::andcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( op1, con, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_reg);
|
|
%}
|
|
|
|
instruct compU_iReg_imm13(flagsRegU icc, iRegI op1, immU12 op2 ) %{
|
|
match(Set icc (CmpU op1 op2));
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2\t! unsigned" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_imm);
|
|
%}
|
|
|
|
instruct compUL_iReg_imm13(flagsRegUL xcc, iRegL op1, immUL12 op2) %{
|
|
match(Set xcc (CmpUL op1 op2));
|
|
effect(DEF xcc, USE op1, USE op2);
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2\t! unsigned long" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode(form3_rs1_simm13_rd(op1, op2, R_G0));
|
|
ins_pipe(ialu_cconly_reg_imm);
|
|
%}
|
|
|
|
// Compare Pointers
|
|
instruct compP_iRegP(flagsRegP pcc, iRegP op1, iRegP op2 ) %{
|
|
match(Set pcc (CmpP op1 op2));
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2\t! ptr" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_reg);
|
|
%}
|
|
|
|
instruct compP_iRegP_imm13(flagsRegP pcc, iRegP op1, immP13 op2 ) %{
|
|
match(Set pcc (CmpP op1 op2));
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2\t! ptr" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_imm);
|
|
%}
|
|
|
|
// Compare Narrow oops
|
|
instruct compN_iRegN(flagsReg icc, iRegN op1, iRegN op2 ) %{
|
|
match(Set icc (CmpN op1 op2));
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2\t! compressed ptr" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_rs2_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_reg);
|
|
%}
|
|
|
|
instruct compN_iRegN_immN0(flagsReg icc, iRegN op1, immN0 op2 ) %{
|
|
match(Set icc (CmpN op1 op2));
|
|
|
|
size(4);
|
|
format %{ "CMP $op1,$op2\t! compressed ptr" %}
|
|
opcode(Assembler::subcc_op3, Assembler::arith_op);
|
|
ins_encode( form3_rs1_simm13_rd( op1, op2, R_G0 ) );
|
|
ins_pipe(ialu_cconly_reg_imm);
|
|
%}
|
|
|
|
//----------Max and Min--------------------------------------------------------
|
|
// Min Instructions
|
|
// Conditional move for min
|
|
instruct cmovI_reg_lt( iRegI op2, iRegI op1, flagsReg icc ) %{
|
|
effect( USE_DEF op2, USE op1, USE icc );
|
|
|
|
size(4);
|
|
format %{ "MOVlt icc,$op1,$op2\t! min" %}
|
|
opcode(Assembler::less);
|
|
ins_encode( enc_cmov_reg_minmax(op2,op1) );
|
|
ins_pipe(ialu_reg_flags);
|
|
%}
|
|
|
|
// Min Register with Register.
|
|
instruct minI_eReg(iRegI op1, iRegI op2) %{
|
|
match(Set op2 (MinI op1 op2));
|
|
ins_cost(DEFAULT_COST*2);
|
|
expand %{
|
|
flagsReg icc;
|
|
compI_iReg(icc,op1,op2);
|
|
cmovI_reg_lt(op2,op1,icc);
|
|
%}
|
|
%}
|
|
|
|
// Max Instructions
|
|
// Conditional move for max
|
|
instruct cmovI_reg_gt( iRegI op2, iRegI op1, flagsReg icc ) %{
|
|
effect( USE_DEF op2, USE op1, USE icc );
|
|
format %{ "MOVgt icc,$op1,$op2\t! max" %}
|
|
opcode(Assembler::greater);
|
|
ins_encode( enc_cmov_reg_minmax(op2,op1) );
|
|
ins_pipe(ialu_reg_flags);
|
|
%}
|
|
|
|
// Max Register with Register
|
|
instruct maxI_eReg(iRegI op1, iRegI op2) %{
|
|
match(Set op2 (MaxI op1 op2));
|
|
ins_cost(DEFAULT_COST*2);
|
|
expand %{
|
|
flagsReg icc;
|
|
compI_iReg(icc,op1,op2);
|
|
cmovI_reg_gt(op2,op1,icc);
|
|
%}
|
|
%}
|
|
|
|
|
|
//----------Float Compares----------------------------------------------------
|
|
// Compare floating, generate condition code
|
|
instruct cmpF_cc(flagsRegF fcc, regF src1, regF src2) %{
|
|
match(Set fcc (CmpF src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FCMPs $fcc,$src1,$src2" %}
|
|
opcode(Assembler::fpop2_op3, Assembler::arith_op, Assembler::fcmps_opf);
|
|
ins_encode( form3_opf_rs1F_rs2F_fcc( src1, src2, fcc ) );
|
|
ins_pipe(faddF_fcc_reg_reg_zero);
|
|
%}
|
|
|
|
instruct cmpD_cc(flagsRegF fcc, regD src1, regD src2) %{
|
|
match(Set fcc (CmpD src1 src2));
|
|
|
|
size(4);
|
|
format %{ "FCMPd $fcc,$src1,$src2" %}
|
|
opcode(Assembler::fpop2_op3, Assembler::arith_op, Assembler::fcmpd_opf);
|
|
ins_encode( form3_opf_rs1D_rs2D_fcc( src1, src2, fcc ) );
|
|
ins_pipe(faddD_fcc_reg_reg_zero);
|
|
%}
|
|
|
|
|
|
// Compare floating, generate -1,0,1
|
|
instruct cmpF_reg(iRegI dst, regF src1, regF src2, flagsRegF0 fcc0) %{
|
|
match(Set dst (CmpF3 src1 src2));
|
|
effect(KILL fcc0);
|
|
ins_cost(DEFAULT_COST*3+BRANCH_COST*3);
|
|
format %{ "fcmpl $dst,$src1,$src2" %}
|
|
// Primary = float
|
|
opcode( true );
|
|
ins_encode( floating_cmp( dst, src1, src2 ) );
|
|
ins_pipe( floating_cmp );
|
|
%}
|
|
|
|
instruct cmpD_reg(iRegI dst, regD src1, regD src2, flagsRegF0 fcc0) %{
|
|
match(Set dst (CmpD3 src1 src2));
|
|
effect(KILL fcc0);
|
|
ins_cost(DEFAULT_COST*3+BRANCH_COST*3);
|
|
format %{ "dcmpl $dst,$src1,$src2" %}
|
|
// Primary = double (not float)
|
|
opcode( false );
|
|
ins_encode( floating_cmp( dst, src1, src2 ) );
|
|
ins_pipe( floating_cmp );
|
|
%}
|
|
|
|
//----------Branches---------------------------------------------------------
|
|
// Jump
|
|
// (compare 'operand indIndex' and 'instruct addP_reg_reg' above)
|
|
instruct jumpXtnd(iRegX switch_val, o7RegI table) %{
|
|
match(Jump switch_val);
|
|
effect(TEMP table);
|
|
|
|
ins_cost(350);
|
|
|
|
format %{ "ADD $constanttablebase, $constantoffset, O7\n\t"
|
|
"LD [O7 + $switch_val], O7\n\t"
|
|
"JUMP O7" %}
|
|
ins_encode %{
|
|
// Calculate table address into a register.
|
|
Register table_reg;
|
|
Register label_reg = O7;
|
|
// If we are calculating the size of this instruction don't trust
|
|
// zero offsets because they might change when
|
|
// MachConstantBaseNode decides to optimize the constant table
|
|
// base.
|
|
if ((constant_offset() == 0) && !Compile::current()->in_scratch_emit_size()) {
|
|
table_reg = $constanttablebase;
|
|
} else {
|
|
table_reg = O7;
|
|
RegisterOrConstant con_offset = __ ensure_simm13_or_reg($constantoffset, O7);
|
|
__ add($constanttablebase, con_offset, table_reg);
|
|
}
|
|
|
|
// Jump to base address + switch value
|
|
__ ld_ptr(table_reg, $switch_val$$Register, label_reg);
|
|
__ jmp(label_reg, G0);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(ialu_reg_reg);
|
|
%}
|
|
|
|
// Direct Branch. Use V8 version with longer range.
|
|
instruct branch(label labl) %{
|
|
match(Goto);
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BA $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
__ ba(*L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br);
|
|
%}
|
|
|
|
// Direct Branch, short with no delay slot
|
|
instruct branch_short(label labl) %{
|
|
match(Goto);
|
|
predicate(UseCBCond);
|
|
effect(USE labl);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BA $labl\t! short branch" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ ba_short(*L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_imm);
|
|
%}
|
|
|
|
// Conditional Direct Branch
|
|
instruct branchCon(cmpOp cmp, flagsReg icc, label labl) %{
|
|
match(If cmp icc);
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BP$cmp $icc,$labl" %}
|
|
// Prim = bits 24-22, Secnd = bits 31-30
|
|
ins_encode( enc_bp( labl, cmp, icc ) );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_cc);
|
|
%}
|
|
|
|
instruct branchConU(cmpOpU cmp, flagsRegU icc, label labl) %{
|
|
match(If cmp icc);
|
|
effect(USE labl);
|
|
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BP$cmp $icc,$labl" %}
|
|
// Prim = bits 24-22, Secnd = bits 31-30
|
|
ins_encode( enc_bp( labl, cmp, icc ) );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_cc);
|
|
%}
|
|
|
|
instruct branchConP(cmpOpP cmp, flagsRegP pcc, label labl) %{
|
|
match(If cmp pcc);
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BP$cmp $pcc,$labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
|
|
__ bp( (Assembler::Condition)($cmp$$cmpcode), false, Assembler::ptr_cc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_cc);
|
|
%}
|
|
|
|
instruct branchConF(cmpOpF cmp, flagsRegF fcc, label labl) %{
|
|
match(If cmp fcc);
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "FBP$cmp $fcc,$labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
|
|
__ fbp( (Assembler::Condition)($cmp$$cmpcode), false, (Assembler::CC)($fcc$$reg), predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_fcc);
|
|
%}
|
|
|
|
instruct branchLoopEnd(cmpOp cmp, flagsReg icc, label labl) %{
|
|
match(CountedLoopEnd cmp icc);
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BP$cmp $icc,$labl\t! Loop end" %}
|
|
// Prim = bits 24-22, Secnd = bits 31-30
|
|
ins_encode( enc_bp( labl, cmp, icc ) );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_cc);
|
|
%}
|
|
|
|
instruct branchLoopEndU(cmpOpU cmp, flagsRegU icc, label labl) %{
|
|
match(CountedLoopEnd cmp icc);
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BP$cmp $icc,$labl\t! Loop end" %}
|
|
// Prim = bits 24-22, Secnd = bits 31-30
|
|
ins_encode( enc_bp( labl, cmp, icc ) );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_cc);
|
|
%}
|
|
|
|
// Compare and branch instructions
|
|
instruct cmpI_reg_branch(cmpOp cmp, iRegI op1, iRegI op2, label labl, flagsReg icc) %{
|
|
match(If cmp (CmpI op1 op2));
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! int\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$Register);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_reg);
|
|
%}
|
|
|
|
instruct cmpI_imm_branch(cmpOp cmp, iRegI op1, immI5 op2, label labl, flagsReg icc) %{
|
|
match(If cmp (CmpI op1 op2));
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! int\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$constant);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_imm);
|
|
%}
|
|
|
|
instruct cmpU_reg_branch(cmpOpU cmp, iRegI op1, iRegI op2, label labl, flagsRegU icc) %{
|
|
match(If cmp (CmpU op1 op2));
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! unsigned\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$Register);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_reg);
|
|
%}
|
|
|
|
instruct cmpU_imm_branch(cmpOpU cmp, iRegI op1, immI5 op2, label labl, flagsRegU icc) %{
|
|
match(If cmp (CmpU op1 op2));
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! unsigned\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$constant);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_imm);
|
|
%}
|
|
|
|
instruct cmpUL_reg_branch(cmpOpU cmp, iRegL op1, iRegL op2, label labl, flagsRegUL xcc) %{
|
|
match(If cmp (CmpUL op1 op2));
|
|
effect(USE labl, KILL xcc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! unsigned long\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$Register);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_reg);
|
|
%}
|
|
|
|
instruct cmpUL_imm_branch(cmpOpU cmp, iRegL op1, immL5 op2, label labl, flagsRegUL xcc) %{
|
|
match(If cmp (CmpUL op1 op2));
|
|
effect(USE labl, KILL xcc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! unsigned long\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$constant);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_imm);
|
|
%}
|
|
|
|
instruct cmpL_reg_branch(cmpOp cmp, iRegL op1, iRegL op2, label labl, flagsRegL xcc) %{
|
|
match(If cmp (CmpL op1 op2));
|
|
effect(USE labl, KILL xcc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! long\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$Register);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_reg);
|
|
%}
|
|
|
|
instruct cmpL_imm_branch(cmpOp cmp, iRegL op1, immL5 op2, label labl, flagsRegL xcc) %{
|
|
match(If cmp (CmpL op1 op2));
|
|
effect(USE labl, KILL xcc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! long\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$constant);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_imm);
|
|
%}
|
|
|
|
// Compare Pointers and branch
|
|
instruct cmpP_reg_branch(cmpOpP cmp, iRegP op1, iRegP op2, label labl, flagsRegP pcc) %{
|
|
match(If cmp (CmpP op1 op2));
|
|
effect(USE labl, KILL pcc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! ptr\n\t"
|
|
"B$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$Register);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::ptr_cc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_reg);
|
|
%}
|
|
|
|
instruct cmpP_null_branch(cmpOpP cmp, iRegP op1, immP0 null, label labl, flagsRegP pcc) %{
|
|
match(If cmp (CmpP op1 null));
|
|
effect(USE labl, KILL pcc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,0\t! ptr\n\t"
|
|
"B$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, G0);
|
|
// bpr() is not used here since it has shorter distance.
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::ptr_cc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_reg);
|
|
%}
|
|
|
|
instruct cmpN_reg_branch(cmpOp cmp, iRegN op1, iRegN op2, label labl, flagsReg icc) %{
|
|
match(If cmp (CmpN op1 op2));
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! compressed ptr\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$Register);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_reg);
|
|
%}
|
|
|
|
instruct cmpN_null_branch(cmpOp cmp, iRegN op1, immN0 null, label labl, flagsReg icc) %{
|
|
match(If cmp (CmpN op1 null));
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,0\t! compressed ptr\n\t"
|
|
"BP$cmp $labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, G0);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_reg);
|
|
%}
|
|
|
|
// Loop back branch
|
|
instruct cmpI_reg_branchLoopEnd(cmpOp cmp, iRegI op1, iRegI op2, label labl, flagsReg icc) %{
|
|
match(CountedLoopEnd cmp (CmpI op1 op2));
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! int\n\t"
|
|
"BP$cmp $labl\t! Loop end" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$Register);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_reg);
|
|
%}
|
|
|
|
instruct cmpI_imm_branchLoopEnd(cmpOp cmp, iRegI op1, immI5 op2, label labl, flagsReg icc) %{
|
|
match(CountedLoopEnd cmp (CmpI op1 op2));
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(12);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CMP $op1,$op2\t! int\n\t"
|
|
"BP$cmp $labl\t! Loop end" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
__ cmp($op1$$Register, $op2$$constant);
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::icc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_pipe(cmp_br_reg_imm);
|
|
%}
|
|
|
|
// Short compare and branch instructions
|
|
instruct cmpI_reg_branch_short(cmpOp cmp, iRegI op1, iRegI op2, label labl, flagsReg icc) %{
|
|
match(If cmp (CmpI op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CWB$cmp $op1,$op2,$labl\t! int" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$Register, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_reg);
|
|
%}
|
|
|
|
instruct cmpI_imm_branch_short(cmpOp cmp, iRegI op1, immI5 op2, label labl, flagsReg icc) %{
|
|
match(If cmp (CmpI op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CWB$cmp $op1,$op2,$labl\t! int" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$constant, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_imm);
|
|
%}
|
|
|
|
instruct cmpU_reg_branch_short(cmpOpU cmp, iRegI op1, iRegI op2, label labl, flagsRegU icc) %{
|
|
match(If cmp (CmpU op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CWB$cmp $op1,$op2,$labl\t! unsigned" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$Register, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_reg);
|
|
%}
|
|
|
|
instruct cmpU_imm_branch_short(cmpOpU cmp, iRegI op1, immI5 op2, label labl, flagsRegU icc) %{
|
|
match(If cmp (CmpU op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CWB$cmp $op1,$op2,$labl\t! unsigned" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$constant, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_imm);
|
|
%}
|
|
|
|
instruct cmpUL_reg_branch_short(cmpOpU cmp, iRegL op1, iRegL op2, label labl, flagsRegUL xcc) %{
|
|
match(If cmp (CmpUL op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL xcc);
|
|
|
|
size(4);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CXB$cmp $op1,$op2,$labl\t! unsigned long" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::xcc, $op1$$Register, $op2$$Register, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_reg);
|
|
%}
|
|
|
|
instruct cmpUL_imm_branch_short(cmpOpU cmp, iRegL op1, immL5 op2, label labl, flagsRegUL xcc) %{
|
|
match(If cmp (CmpUL op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL xcc);
|
|
|
|
size(4);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CXB$cmp $op1,$op2,$labl\t! unsigned long" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::xcc, $op1$$Register, $op2$$constant, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_imm);
|
|
%}
|
|
|
|
instruct cmpL_reg_branch_short(cmpOp cmp, iRegL op1, iRegL op2, label labl, flagsRegL xcc) %{
|
|
match(If cmp (CmpL op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL xcc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CXB$cmp $op1,$op2,$labl\t! long" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::xcc, $op1$$Register, $op2$$Register, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_reg);
|
|
%}
|
|
|
|
instruct cmpL_imm_branch_short(cmpOp cmp, iRegL op1, immL5 op2, label labl, flagsRegL xcc) %{
|
|
match(If cmp (CmpL op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL xcc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CXB$cmp $op1,$op2,$labl\t! long" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::xcc, $op1$$Register, $op2$$constant, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_imm);
|
|
%}
|
|
|
|
// Compare Pointers and branch
|
|
instruct cmpP_reg_branch_short(cmpOpP cmp, iRegP op1, iRegP op2, label labl, flagsRegP pcc) %{
|
|
match(If cmp (CmpP op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL pcc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CXB$cmp $op1,$op2,$labl\t! ptr" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::ptr_cc, $op1$$Register, $op2$$Register, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_reg);
|
|
%}
|
|
|
|
instruct cmpP_null_branch_short(cmpOpP cmp, iRegP op1, immP0 null, label labl, flagsRegP pcc) %{
|
|
match(If cmp (CmpP op1 null));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL pcc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CXB$cmp $op1,0,$labl\t! ptr" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::ptr_cc, $op1$$Register, G0, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_reg);
|
|
%}
|
|
|
|
instruct cmpN_reg_branch_short(cmpOp cmp, iRegN op1, iRegN op2, label labl, flagsReg icc) %{
|
|
match(If cmp (CmpN op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CWB$cmp $op1,$op2,$labl\t! compressed ptr" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$Register, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_reg);
|
|
%}
|
|
|
|
instruct cmpN_null_branch_short(cmpOp cmp, iRegN op1, immN0 null, label labl, flagsReg icc) %{
|
|
match(If cmp (CmpN op1 null));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CWB$cmp $op1,0,$labl\t! compressed ptr" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, G0, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_reg);
|
|
%}
|
|
|
|
// Loop back branch
|
|
instruct cmpI_reg_branchLoopEnd_short(cmpOp cmp, iRegI op1, iRegI op2, label labl, flagsReg icc) %{
|
|
match(CountedLoopEnd cmp (CmpI op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CWB$cmp $op1,$op2,$labl\t! Loop end" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$Register, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_reg);
|
|
%}
|
|
|
|
instruct cmpI_imm_branchLoopEnd_short(cmpOp cmp, iRegI op1, immI5 op2, label labl, flagsReg icc) %{
|
|
match(CountedLoopEnd cmp (CmpI op1 op2));
|
|
predicate(UseCBCond);
|
|
effect(USE labl, KILL icc);
|
|
|
|
size(4); // Assuming no NOP inserted.
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "CWB$cmp $op1,$op2,$labl\t! Loop end" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
assert(__ use_cbcond(*L), "back to back cbcond");
|
|
__ cbcond((Assembler::Condition)($cmp$$cmpcode), Assembler::icc, $op1$$Register, $op2$$constant, *L);
|
|
%}
|
|
ins_short_branch(1);
|
|
ins_avoid_back_to_back(AVOID_BEFORE_AND_AFTER);
|
|
ins_pipe(cbcond_reg_imm);
|
|
%}
|
|
|
|
// Branch-on-register tests all 64 bits. We assume that values
|
|
// in 64-bit registers always remains zero or sign extended
|
|
// unless our code munges the high bits. Interrupts can chop
|
|
// the high order bits to zero or sign at any time.
|
|
instruct branchCon_regI(cmpOp_reg cmp, iRegI op1, immI0 zero, label labl) %{
|
|
match(If cmp (CmpI op1 zero));
|
|
predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf));
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BR$cmp $op1,$labl" %}
|
|
ins_encode( enc_bpr( labl, cmp, op1 ) );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_reg);
|
|
%}
|
|
|
|
instruct branchCon_regP(cmpOp_reg cmp, iRegP op1, immP0 null, label labl) %{
|
|
match(If cmp (CmpP op1 null));
|
|
predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf));
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BR$cmp $op1,$labl" %}
|
|
ins_encode( enc_bpr( labl, cmp, op1 ) );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_reg);
|
|
%}
|
|
|
|
instruct branchCon_regL(cmpOp_reg cmp, iRegL op1, immL0 zero, label labl) %{
|
|
match(If cmp (CmpL op1 zero));
|
|
predicate(can_branch_register(_kids[0]->_leaf, _kids[1]->_leaf));
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BR$cmp $op1,$labl" %}
|
|
ins_encode( enc_bpr( labl, cmp, op1 ) );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_reg);
|
|
%}
|
|
|
|
|
|
// ============================================================================
|
|
// Long Compare
|
|
//
|
|
// Currently we hold longs in 2 registers. Comparing such values efficiently
|
|
// is tricky. The flavor of compare used depends on whether we are testing
|
|
// for LT, LE, or EQ. For a simple LT test we can check just the sign bit.
|
|
// The GE test is the negated LT test. The LE test can be had by commuting
|
|
// the operands (yielding a GE test) and then negating; negate again for the
|
|
// GT test. The EQ test is done by ORcc'ing the high and low halves, and the
|
|
// NE test is negated from that.
|
|
|
|
// Due to a shortcoming in the ADLC, it mixes up expressions like:
|
|
// (foo (CmpI (CmpL X Y) 0)) and (bar (CmpI (CmpL X 0L) 0)). Note the
|
|
// difference between 'Y' and '0L'. The tree-matches for the CmpI sections
|
|
// are collapsed internally in the ADLC's dfa-gen code. The match for
|
|
// (CmpI (CmpL X Y) 0) is silently replaced with (CmpI (CmpL X 0L) 0) and the
|
|
// foo match ends up with the wrong leaf. One fix is to not match both
|
|
// reg-reg and reg-zero forms of long-compare. This is unfortunate because
|
|
// both forms beat the trinary form of long-compare and both are very useful
|
|
// on Intel which has so few registers.
|
|
|
|
instruct branchCon_long(cmpOp cmp, flagsRegL xcc, label labl) %{
|
|
match(If cmp xcc);
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BP$cmp $xcc,$labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
|
|
__ bp( (Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_cc);
|
|
%}
|
|
|
|
instruct branchConU_long(cmpOpU cmp, flagsRegUL xcc, label labl) %{
|
|
match(If cmp xcc);
|
|
effect(USE labl);
|
|
|
|
size(8);
|
|
ins_cost(BRANCH_COST);
|
|
format %{ "BP$cmp $xcc,$labl" %}
|
|
ins_encode %{
|
|
Label* L = $labl$$label;
|
|
Assembler::Predict predict_taken =
|
|
cbuf.is_backward_branch(*L) ? Assembler::pt : Assembler::pn;
|
|
|
|
__ bp((Assembler::Condition)($cmp$$cmpcode), false, Assembler::xcc, predict_taken, *L);
|
|
__ delayed()->nop();
|
|
%}
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(br_cc);
|
|
%}
|
|
|
|
// Manifest a CmpL3 result in an integer register. Very painful.
|
|
// This is the test to avoid.
|
|
instruct cmpL3_reg_reg(iRegI dst, iRegL src1, iRegL src2, flagsReg ccr ) %{
|
|
match(Set dst (CmpL3 src1 src2) );
|
|
effect( KILL ccr );
|
|
ins_cost(6*DEFAULT_COST);
|
|
size(24);
|
|
format %{ "CMP $src1,$src2\t\t! long\n"
|
|
"\tBLT,a,pn done\n"
|
|
"\tMOV -1,$dst\t! delay slot\n"
|
|
"\tBGT,a,pn done\n"
|
|
"\tMOV 1,$dst\t! delay slot\n"
|
|
"\tCLR $dst\n"
|
|
"done:" %}
|
|
ins_encode( cmpl_flag(src1,src2,dst) );
|
|
ins_pipe(cmpL_reg);
|
|
%}
|
|
|
|
// Conditional move
|
|
instruct cmovLL_reg(cmpOp cmp, flagsRegL xcc, iRegL dst, iRegL src) %{
|
|
match(Set dst (CMoveL (Binary cmp xcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
format %{ "MOV$cmp $xcc,$src,$dst\t! long" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::xcc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovLL_imm(cmpOp cmp, flagsRegL xcc, iRegL dst, immL0 src) %{
|
|
match(Set dst (CMoveL (Binary cmp xcc) (Binary dst src)));
|
|
ins_cost(140);
|
|
format %{ "MOV$cmp $xcc,$src,$dst\t! long" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::xcc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovIL_reg(cmpOp cmp, flagsRegL xcc, iRegI dst, iRegI src) %{
|
|
match(Set dst (CMoveI (Binary cmp xcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
format %{ "MOV$cmp $xcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::xcc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovIL_imm(cmpOp cmp, flagsRegL xcc, iRegI dst, immI11 src) %{
|
|
match(Set dst (CMoveI (Binary cmp xcc) (Binary dst src)));
|
|
ins_cost(140);
|
|
format %{ "MOV$cmp $xcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::xcc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovNL_reg(cmpOp cmp, flagsRegL xcc, iRegN dst, iRegN src) %{
|
|
match(Set dst (CMoveN (Binary cmp xcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
format %{ "MOV$cmp $xcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::xcc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovPL_reg(cmpOp cmp, flagsRegL xcc, iRegP dst, iRegP src) %{
|
|
match(Set dst (CMoveP (Binary cmp xcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
format %{ "MOV$cmp $xcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_reg(cmp,dst,src, (Assembler::xcc)) );
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct cmovPL_imm(cmpOp cmp, flagsRegL xcc, iRegP dst, immP0 src) %{
|
|
match(Set dst (CMoveP (Binary cmp xcc) (Binary dst src)));
|
|
ins_cost(140);
|
|
format %{ "MOV$cmp $xcc,$src,$dst" %}
|
|
ins_encode( enc_cmov_imm(cmp,dst,src, (Assembler::xcc)) );
|
|
ins_pipe(ialu_imm);
|
|
%}
|
|
|
|
instruct cmovFL_reg(cmpOp cmp, flagsRegL xcc, regF dst, regF src) %{
|
|
match(Set dst (CMoveF (Binary cmp xcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
opcode(0x101);
|
|
format %{ "FMOVS$cmp $xcc,$src,$dst" %}
|
|
ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::xcc)) );
|
|
ins_pipe(int_conditional_float_move);
|
|
%}
|
|
|
|
instruct cmovDL_reg(cmpOp cmp, flagsRegL xcc, regD dst, regD src) %{
|
|
match(Set dst (CMoveD (Binary cmp xcc) (Binary dst src)));
|
|
ins_cost(150);
|
|
opcode(0x102);
|
|
format %{ "FMOVD$cmp $xcc,$src,$dst" %}
|
|
ins_encode( enc_cmovf_reg(cmp,dst,src, (Assembler::xcc)) );
|
|
ins_pipe(int_conditional_float_move);
|
|
%}
|
|
|
|
// ============================================================================
|
|
// Safepoint Instruction
|
|
instruct safePoint_poll(iRegP poll) %{
|
|
match(SafePoint poll);
|
|
effect(USE poll);
|
|
|
|
size(4);
|
|
format %{ "LDX [$poll],R_G0\t! Safepoint: poll for GC" %}
|
|
ins_encode %{
|
|
__ relocate(relocInfo::poll_type);
|
|
__ ld_ptr($poll$$Register, 0, G0);
|
|
%}
|
|
ins_pipe(loadPollP);
|
|
%}
|
|
|
|
// ============================================================================
|
|
// Call Instructions
|
|
// Call Java Static Instruction
|
|
instruct CallStaticJavaDirect( method meth ) %{
|
|
match(CallStaticJava);
|
|
predicate(! ((CallStaticJavaNode*)n)->is_method_handle_invoke());
|
|
effect(USE meth);
|
|
|
|
size(8);
|
|
ins_cost(CALL_COST);
|
|
format %{ "CALL,static ; NOP ==> " %}
|
|
ins_encode( Java_Static_Call( meth ), call_epilog );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(simple_call);
|
|
%}
|
|
|
|
// Call Java Static Instruction (method handle version)
|
|
instruct CallStaticJavaHandle(method meth, l7RegP l7_mh_SP_save) %{
|
|
match(CallStaticJava);
|
|
predicate(((CallStaticJavaNode*)n)->is_method_handle_invoke());
|
|
effect(USE meth, KILL l7_mh_SP_save);
|
|
|
|
size(16);
|
|
ins_cost(CALL_COST);
|
|
format %{ "CALL,static/MethodHandle" %}
|
|
ins_encode(preserve_SP, Java_Static_Call(meth), restore_SP, call_epilog);
|
|
ins_pipe(simple_call);
|
|
%}
|
|
|
|
// Call Java Dynamic Instruction
|
|
instruct CallDynamicJavaDirect( method meth ) %{
|
|
match(CallDynamicJava);
|
|
effect(USE meth);
|
|
|
|
ins_cost(CALL_COST);
|
|
format %{ "SET (empty),R_G5\n\t"
|
|
"CALL,dynamic ; NOP ==> " %}
|
|
ins_encode( Java_Dynamic_Call( meth ), call_epilog );
|
|
ins_pipe(call);
|
|
%}
|
|
|
|
// Call Runtime Instruction
|
|
instruct CallRuntimeDirect(method meth, l7RegP l7) %{
|
|
match(CallRuntime);
|
|
effect(USE meth, KILL l7);
|
|
ins_cost(CALL_COST);
|
|
format %{ "CALL,runtime" %}
|
|
ins_encode( Java_To_Runtime( meth ),
|
|
call_epilog, adjust_long_from_native_call );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(simple_call);
|
|
%}
|
|
|
|
// Call runtime without safepoint - same as CallRuntime
|
|
instruct CallLeafDirect(method meth, l7RegP l7) %{
|
|
match(CallLeaf);
|
|
effect(USE meth, KILL l7);
|
|
ins_cost(CALL_COST);
|
|
format %{ "CALL,runtime leaf" %}
|
|
ins_encode( Java_To_Runtime( meth ),
|
|
call_epilog,
|
|
adjust_long_from_native_call );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(simple_call);
|
|
%}
|
|
|
|
// Call runtime without safepoint - same as CallLeaf
|
|
instruct CallLeafNoFPDirect(method meth, l7RegP l7) %{
|
|
match(CallLeafNoFP);
|
|
effect(USE meth, KILL l7);
|
|
ins_cost(CALL_COST);
|
|
format %{ "CALL,runtime leaf nofp" %}
|
|
ins_encode( Java_To_Runtime( meth ),
|
|
call_epilog,
|
|
adjust_long_from_native_call );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(simple_call);
|
|
%}
|
|
|
|
// Tail Call; Jump from runtime stub to Java code.
|
|
// Also known as an 'interprocedural jump'.
|
|
// Target of jump will eventually return to caller.
|
|
// TailJump below removes the return address.
|
|
instruct TailCalljmpInd(g3RegP jump_target, inline_cache_regP method_oop) %{
|
|
match(TailCall jump_target method_oop );
|
|
|
|
ins_cost(CALL_COST);
|
|
format %{ "Jmp $jump_target ; NOP \t! $method_oop holds method oop" %}
|
|
ins_encode(form_jmpl(jump_target));
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(tail_call);
|
|
%}
|
|
|
|
|
|
// Return Instruction
|
|
instruct Ret() %{
|
|
match(Return);
|
|
|
|
// The epilogue node did the ret already.
|
|
size(0);
|
|
format %{ "! return" %}
|
|
ins_encode();
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
|
|
// Tail Jump; remove the return address; jump to target.
|
|
// TailCall above leaves the return address around.
|
|
// TailJump is used in only one place, the rethrow_Java stub (fancy_jump=2).
|
|
// ex_oop (Exception Oop) is needed in %o0 at the jump. As there would be a
|
|
// "restore" before this instruction (in Epilogue), we need to materialize it
|
|
// in %i0.
|
|
instruct tailjmpInd(g1RegP jump_target, i0RegP ex_oop) %{
|
|
match( TailJump jump_target ex_oop );
|
|
ins_cost(CALL_COST);
|
|
format %{ "! discard R_O7\n\t"
|
|
"Jmp $jump_target ; ADD O7,8,O1 \t! $ex_oop holds exc. oop" %}
|
|
ins_encode(form_jmpl_set_exception_pc(jump_target));
|
|
// opcode(Assembler::jmpl_op3, Assembler::arith_op);
|
|
// The hack duplicates the exception oop into G3, so that CreateEx can use it there.
|
|
// ins_encode( form3_rs1_simm13_rd( jump_target, 0x00, R_G0 ), move_return_pc_to_o1() );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(tail_call);
|
|
%}
|
|
|
|
// Create exception oop: created by stack-crawling runtime code.
|
|
// Created exception is now available to this handler, and is setup
|
|
// just prior to jumping to this handler. No code emitted.
|
|
instruct CreateException( o0RegP ex_oop )
|
|
%{
|
|
match(Set ex_oop (CreateEx));
|
|
ins_cost(0);
|
|
|
|
size(0);
|
|
// use the following format syntax
|
|
format %{ "! exception oop is in R_O0; no code emitted" %}
|
|
ins_encode();
|
|
ins_pipe(empty);
|
|
%}
|
|
|
|
|
|
// Rethrow exception:
|
|
// The exception oop will come in the first argument position.
|
|
// Then JUMP (not call) to the rethrow stub code.
|
|
instruct RethrowException()
|
|
%{
|
|
match(Rethrow);
|
|
ins_cost(CALL_COST);
|
|
|
|
// use the following format syntax
|
|
format %{ "Jmp rethrow_stub" %}
|
|
ins_encode(enc_rethrow);
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(tail_call);
|
|
%}
|
|
|
|
|
|
// Die now
|
|
instruct ShouldNotReachHere( )
|
|
%{
|
|
match(Halt);
|
|
ins_cost(CALL_COST);
|
|
|
|
size(4);
|
|
// Use the following format syntax
|
|
format %{ "ILLTRAP ; ShouldNotReachHere" %}
|
|
ins_encode( form2_illtrap() );
|
|
ins_pipe(tail_call);
|
|
%}
|
|
|
|
// ============================================================================
|
|
// The 2nd slow-half of a subtype check. Scan the subklass's 2ndary superklass
|
|
// array for an instance of the superklass. Set a hidden internal cache on a
|
|
// hit (cache is checked with exposed code in gen_subtype_check()). Return
|
|
// not zero for a miss or zero for a hit. The encoding ALSO sets flags.
|
|
instruct partialSubtypeCheck( o0RegP index, o1RegP sub, o2RegP super, flagsRegP pcc, o7RegP o7 ) %{
|
|
match(Set index (PartialSubtypeCheck sub super));
|
|
effect( KILL pcc, KILL o7 );
|
|
ins_cost(DEFAULT_COST*10);
|
|
format %{ "CALL PartialSubtypeCheck\n\tNOP" %}
|
|
ins_encode( enc_PartialSubtypeCheck() );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(partial_subtype_check_pipe);
|
|
%}
|
|
|
|
instruct partialSubtypeCheck_vs_zero( flagsRegP pcc, o1RegP sub, o2RegP super, immP0 zero, o0RegP idx, o7RegP o7 ) %{
|
|
match(Set pcc (CmpP (PartialSubtypeCheck sub super) zero));
|
|
effect( KILL idx, KILL o7 );
|
|
ins_cost(DEFAULT_COST*10);
|
|
format %{ "CALL PartialSubtypeCheck\n\tNOP\t# (sets condition codes)" %}
|
|
ins_encode( enc_PartialSubtypeCheck() );
|
|
ins_avoid_back_to_back(AVOID_BEFORE);
|
|
ins_pipe(partial_subtype_check_pipe);
|
|
%}
|
|
|
|
|
|
// ============================================================================
|
|
// inlined locking and unlocking
|
|
|
|
instruct cmpFastLock(flagsRegP pcc, iRegP object, o1RegP box, iRegP scratch2, o7RegP scratch ) %{
|
|
match(Set pcc (FastLock object box));
|
|
|
|
effect(TEMP scratch2, USE_KILL box, KILL scratch);
|
|
ins_cost(100);
|
|
|
|
format %{ "FASTLOCK $object,$box\t! kills $box,$scratch,$scratch2" %}
|
|
ins_encode( Fast_Lock(object, box, scratch, scratch2) );
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
|
|
instruct cmpFastUnlock(flagsRegP pcc, iRegP object, o1RegP box, iRegP scratch2, o7RegP scratch ) %{
|
|
match(Set pcc (FastUnlock object box));
|
|
effect(TEMP scratch2, USE_KILL box, KILL scratch);
|
|
ins_cost(100);
|
|
|
|
format %{ "FASTUNLOCK $object,$box\t! kills $box,$scratch,$scratch2" %}
|
|
ins_encode( Fast_Unlock(object, box, scratch, scratch2) );
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
// The encodings are generic.
|
|
instruct clear_array(iRegX cnt, iRegP base, iRegX temp, Universe dummy, flagsReg ccr) %{
|
|
predicate(!use_block_zeroing(n->in(2)) );
|
|
match(Set dummy (ClearArray cnt base));
|
|
effect(TEMP temp, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "MOV $cnt,$temp\n"
|
|
"loop: SUBcc $temp,8,$temp\t! Count down a dword of bytes\n"
|
|
" BRge loop\t\t! Clearing loop\n"
|
|
" STX G0,[$base+$temp]\t! delay slot" %}
|
|
|
|
ins_encode %{
|
|
// Compiler ensures base is doubleword aligned and cnt is count of doublewords
|
|
Register nof_bytes_arg = $cnt$$Register;
|
|
Register nof_bytes_tmp = $temp$$Register;
|
|
Register base_pointer_arg = $base$$Register;
|
|
|
|
Label loop;
|
|
__ mov(nof_bytes_arg, nof_bytes_tmp);
|
|
|
|
// Loop and clear, walking backwards through the array.
|
|
// nof_bytes_tmp (if >0) is always the number of bytes to zero
|
|
__ bind(loop);
|
|
__ deccc(nof_bytes_tmp, 8);
|
|
__ br(Assembler::greaterEqual, true, Assembler::pt, loop);
|
|
__ delayed()-> stx(G0, base_pointer_arg, nof_bytes_tmp);
|
|
// %%%% this mini-loop must not cross a cache boundary!
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct clear_array_bis(g1RegX cnt, o0RegP base, Universe dummy, flagsReg ccr) %{
|
|
predicate(use_block_zeroing(n->in(2)));
|
|
match(Set dummy (ClearArray cnt base));
|
|
effect(USE_KILL cnt, USE_KILL base, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "CLEAR [$base, $cnt]\t! ClearArray" %}
|
|
|
|
ins_encode %{
|
|
|
|
assert(MinObjAlignmentInBytes >= BytesPerLong, "need alternate implementation");
|
|
Register to = $base$$Register;
|
|
Register count = $cnt$$Register;
|
|
|
|
Label Ldone;
|
|
__ nop(); // Separate short branches
|
|
// Use BIS for zeroing (temp is not used).
|
|
__ bis_zeroing(to, count, G0, Ldone);
|
|
__ bind(Ldone);
|
|
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct clear_array_bis_2(g1RegX cnt, o0RegP base, iRegX tmp, Universe dummy, flagsReg ccr) %{
|
|
predicate(use_block_zeroing(n->in(2)) && !Assembler::is_simm13((int)BlockZeroingLowLimit));
|
|
match(Set dummy (ClearArray cnt base));
|
|
effect(TEMP tmp, USE_KILL cnt, USE_KILL base, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "CLEAR [$base, $cnt]\t! ClearArray" %}
|
|
|
|
ins_encode %{
|
|
|
|
assert(MinObjAlignmentInBytes >= BytesPerLong, "need alternate implementation");
|
|
Register to = $base$$Register;
|
|
Register count = $cnt$$Register;
|
|
Register temp = $tmp$$Register;
|
|
|
|
Label Ldone;
|
|
__ nop(); // Separate short branches
|
|
// Use BIS for zeroing
|
|
__ bis_zeroing(to, count, temp, Ldone);
|
|
__ bind(Ldone);
|
|
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct string_compareL(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result,
|
|
o7RegI tmp, flagsReg ccr) %{
|
|
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL);
|
|
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
|
effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp);
|
|
ins_cost(300);
|
|
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %}
|
|
ins_encode %{
|
|
__ string_compare($str1$$Register, $str2$$Register,
|
|
$cnt1$$Register, $cnt2$$Register,
|
|
$tmp$$Register, $tmp$$Register,
|
|
$result$$Register, StrIntrinsicNode::LL);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct string_compareU(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result,
|
|
o7RegI tmp, flagsReg ccr) %{
|
|
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU);
|
|
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
|
effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp);
|
|
ins_cost(300);
|
|
format %{ "String Compare char[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp" %}
|
|
ins_encode %{
|
|
__ string_compare($str1$$Register, $str2$$Register,
|
|
$cnt1$$Register, $cnt2$$Register,
|
|
$tmp$$Register, $tmp$$Register,
|
|
$result$$Register, StrIntrinsicNode::UU);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct string_compareLU(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result,
|
|
o7RegI tmp1, g1RegI tmp2, flagsReg ccr) %{
|
|
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU);
|
|
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
|
effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp1, KILL tmp2);
|
|
ins_cost(300);
|
|
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1,$tmp2" %}
|
|
ins_encode %{
|
|
__ string_compare($str1$$Register, $str2$$Register,
|
|
$cnt1$$Register, $cnt2$$Register,
|
|
$tmp1$$Register, $tmp2$$Register,
|
|
$result$$Register, StrIntrinsicNode::LU);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct string_compareUL(o0RegP str1, o1RegP str2, g3RegI cnt1, g4RegI cnt2, notemp_iRegI result,
|
|
o7RegI tmp1, g1RegI tmp2, flagsReg ccr) %{
|
|
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL);
|
|
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
|
effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL ccr, KILL tmp1, KILL tmp2);
|
|
ins_cost(300);
|
|
format %{ "String Compare byte[] $str1,$cnt1,$str2,$cnt2 -> $result // KILL $tmp1,$tmp2" %}
|
|
ins_encode %{
|
|
__ string_compare($str2$$Register, $str1$$Register,
|
|
$cnt2$$Register, $cnt1$$Register,
|
|
$tmp1$$Register, $tmp2$$Register,
|
|
$result$$Register, StrIntrinsicNode::UL);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct string_equalsL(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result,
|
|
o7RegI tmp, flagsReg ccr) %{
|
|
predicate(((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::LL);
|
|
match(Set result (StrEquals (Binary str1 str2) cnt));
|
|
effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL tmp, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "String Equals byte[] $str1,$str2,$cnt -> $result // KILL $tmp" %}
|
|
ins_encode %{
|
|
__ array_equals(false, $str1$$Register, $str2$$Register,
|
|
$cnt$$Register, $tmp$$Register,
|
|
$result$$Register, true /* byte */);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct string_equalsU(o0RegP str1, o1RegP str2, g3RegI cnt, notemp_iRegI result,
|
|
o7RegI tmp, flagsReg ccr) %{
|
|
predicate(((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::UU);
|
|
match(Set result (StrEquals (Binary str1 str2) cnt));
|
|
effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL tmp, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "String Equals char[] $str1,$str2,$cnt -> $result // KILL $tmp" %}
|
|
ins_encode %{
|
|
__ array_equals(false, $str1$$Register, $str2$$Register,
|
|
$cnt$$Register, $tmp$$Register,
|
|
$result$$Register, false /* byte */);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct array_equalsB(o0RegP ary1, o1RegP ary2, g3RegI tmp1, notemp_iRegI result,
|
|
o7RegI tmp2, flagsReg ccr) %{
|
|
predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::LL);
|
|
match(Set result (AryEq ary1 ary2));
|
|
effect(USE_KILL ary1, USE_KILL ary2, KILL tmp1, KILL tmp2, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1,$tmp2" %}
|
|
ins_encode %{
|
|
__ array_equals(true, $ary1$$Register, $ary2$$Register,
|
|
$tmp1$$Register, $tmp2$$Register,
|
|
$result$$Register, true /* byte */);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct array_equalsC(o0RegP ary1, o1RegP ary2, g3RegI tmp1, notemp_iRegI result,
|
|
o7RegI tmp2, flagsReg ccr) %{
|
|
predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::UU);
|
|
match(Set result (AryEq ary1 ary2));
|
|
effect(USE_KILL ary1, USE_KILL ary2, KILL tmp1, KILL tmp2, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "Array Equals $ary1,$ary2 -> $result // KILL $tmp1,$tmp2" %}
|
|
ins_encode %{
|
|
__ array_equals(true, $ary1$$Register, $ary2$$Register,
|
|
$tmp1$$Register, $tmp2$$Register,
|
|
$result$$Register, false /* byte */);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
instruct has_negatives(o0RegP pAryR, g3RegI iSizeR, notemp_iRegI resultR,
|
|
iRegL tmp1L, iRegL tmp2L, iRegL tmp3L, iRegL tmp4L,
|
|
flagsReg ccr)
|
|
%{
|
|
match(Set resultR (HasNegatives pAryR iSizeR));
|
|
effect(TEMP resultR, TEMP tmp1L, TEMP tmp2L, TEMP tmp3L, TEMP tmp4L, USE pAryR, USE iSizeR, KILL ccr);
|
|
format %{ "has negatives byte[] $pAryR,$iSizeR -> $resultR // KILL $tmp1L,$tmp2L,$tmp3L,$tmp4L" %}
|
|
ins_encode %{
|
|
__ has_negatives($pAryR$$Register, $iSizeR$$Register,
|
|
$resultR$$Register,
|
|
$tmp1L$$Register, $tmp2L$$Register,
|
|
$tmp3L$$Register, $tmp4L$$Register);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
// char[] to byte[] compression
|
|
instruct string_compress(o0RegP src, o1RegP dst, g3RegI len, notemp_iRegI result, iRegL tmp, flagsReg ccr) %{
|
|
predicate(UseVIS < 3);
|
|
match(Set result (StrCompressedCopy src (Binary dst len)));
|
|
effect(TEMP result, TEMP tmp, USE_KILL src, USE_KILL dst, USE_KILL len, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "String Compress $src,$dst,$len -> $result // KILL $tmp" %}
|
|
ins_encode %{
|
|
Label Ldone;
|
|
__ signx($len$$Register);
|
|
__ cmp_zero_and_br(Assembler::zero, $len$$Register, Ldone, false, Assembler::pn);
|
|
__ delayed()->mov($len$$Register, $result$$Register); // copy count
|
|
__ string_compress($src$$Register, $dst$$Register, $len$$Register, $result$$Register, $tmp$$Register, Ldone);
|
|
__ bind(Ldone);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
// fast char[] to byte[] compression using VIS instructions
|
|
instruct string_compress_fast(o0RegP src, o1RegP dst, g3RegI len, notemp_iRegI result,
|
|
iRegL tmp1, iRegL tmp2, iRegL tmp3, iRegL tmp4,
|
|
regD ftmp1, regD ftmp2, regD ftmp3, flagsReg ccr) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set result (StrCompressedCopy src (Binary dst len)));
|
|
effect(TEMP result, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP ftmp1, TEMP ftmp2, TEMP ftmp3, USE_KILL src, USE_KILL dst, USE_KILL len, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "String Compress Fast $src,$dst,$len -> $result // KILL $tmp1,$tmp2,$tmp3,$tmp4,$ftmp1,$ftmp2,$ftmp3" %}
|
|
ins_encode %{
|
|
Label Ldone;
|
|
__ signx($len$$Register);
|
|
__ string_compress_16($src$$Register, $dst$$Register, $len$$Register, $result$$Register,
|
|
$tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register,
|
|
$ftmp1$$FloatRegister, $ftmp2$$FloatRegister, $ftmp3$$FloatRegister, Ldone);
|
|
__ cmp_and_brx_short($len$$Register, 0, Assembler::equal, Assembler::pn, Ldone);
|
|
__ string_compress($src$$Register, $dst$$Register, $len$$Register, $result$$Register, $tmp1$$Register, Ldone);
|
|
__ bind(Ldone);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
// byte[] to char[] inflation
|
|
instruct string_inflate(Universe dummy, o0RegP src, o1RegP dst, g3RegI len,
|
|
iRegL tmp, flagsReg ccr) %{
|
|
match(Set dummy (StrInflatedCopy src (Binary dst len)));
|
|
effect(TEMP tmp, USE_KILL src, USE_KILL dst, USE_KILL len, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "String Inflate $src,$dst,$len // KILL $tmp" %}
|
|
ins_encode %{
|
|
Label Ldone;
|
|
__ signx($len$$Register);
|
|
__ cmp_and_brx_short($len$$Register, 0, Assembler::equal, Assembler::pn, Ldone);
|
|
__ string_inflate($src$$Register, $dst$$Register, $len$$Register, $tmp$$Register, Ldone);
|
|
__ bind(Ldone);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
// fast byte[] to char[] inflation using VIS instructions
|
|
instruct string_inflate_fast(Universe dummy, o0RegP src, o1RegP dst, g3RegI len,
|
|
iRegL tmp, regD ftmp1, regD ftmp2, regD ftmp3, regD ftmp4, flagsReg ccr) %{
|
|
predicate(UseVIS >= 3);
|
|
match(Set dummy (StrInflatedCopy src (Binary dst len)));
|
|
effect(TEMP tmp, TEMP ftmp1, TEMP ftmp2, TEMP ftmp3, TEMP ftmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL ccr);
|
|
ins_cost(300);
|
|
format %{ "String Inflate Fast $src,$dst,$len // KILL $tmp,$ftmp1,$ftmp2,$ftmp3,$ftmp4" %}
|
|
ins_encode %{
|
|
Label Ldone;
|
|
__ signx($len$$Register);
|
|
__ string_inflate_16($src$$Register, $dst$$Register, $len$$Register, $tmp$$Register,
|
|
$ftmp1$$FloatRegister, $ftmp2$$FloatRegister, $ftmp3$$FloatRegister, $ftmp4$$FloatRegister, Ldone);
|
|
__ cmp_and_brx_short($len$$Register, 0, Assembler::equal, Assembler::pn, Ldone);
|
|
__ string_inflate($src$$Register, $dst$$Register, $len$$Register, $tmp$$Register, Ldone);
|
|
__ bind(Ldone);
|
|
%}
|
|
ins_pipe(long_memory_op);
|
|
%}
|
|
|
|
|
|
//---------- Zeros Count Instructions ------------------------------------------
|
|
|
|
instruct countLeadingZerosI(iRegIsafe dst, iRegI src, iRegI tmp, flagsReg cr) %{
|
|
predicate(UsePopCountInstruction); // See Matcher::match_rule_supported
|
|
match(Set dst (CountLeadingZerosI src));
|
|
effect(TEMP dst, TEMP tmp, KILL cr);
|
|
|
|
// x |= (x >> 1);
|
|
// x |= (x >> 2);
|
|
// x |= (x >> 4);
|
|
// x |= (x >> 8);
|
|
// x |= (x >> 16);
|
|
// return (WORDBITS - popc(x));
|
|
format %{ "SRL $src,1,$tmp\t! count leading zeros (int)\n\t"
|
|
"SRL $src,0,$dst\t! 32-bit zero extend\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"SRL $dst,2,$tmp\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"SRL $dst,4,$tmp\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"SRL $dst,8,$tmp\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"SRL $dst,16,$tmp\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"POPC $dst,$dst\n\t"
|
|
"MOV 32,$tmp\n\t"
|
|
"SUB $tmp,$dst,$dst" %}
|
|
ins_encode %{
|
|
Register Rdst = $dst$$Register;
|
|
Register Rsrc = $src$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
__ srl(Rsrc, 1, Rtmp);
|
|
__ srl(Rsrc, 0, Rdst);
|
|
__ or3(Rdst, Rtmp, Rdst);
|
|
__ srl(Rdst, 2, Rtmp);
|
|
__ or3(Rdst, Rtmp, Rdst);
|
|
__ srl(Rdst, 4, Rtmp);
|
|
__ or3(Rdst, Rtmp, Rdst);
|
|
__ srl(Rdst, 8, Rtmp);
|
|
__ or3(Rdst, Rtmp, Rdst);
|
|
__ srl(Rdst, 16, Rtmp);
|
|
__ or3(Rdst, Rtmp, Rdst);
|
|
__ popc(Rdst, Rdst);
|
|
__ mov(BitsPerInt, Rtmp);
|
|
__ sub(Rtmp, Rdst, Rdst);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct countLeadingZerosL(iRegIsafe dst, iRegL src, iRegL tmp, flagsReg cr) %{
|
|
predicate(UsePopCountInstruction); // See Matcher::match_rule_supported
|
|
match(Set dst (CountLeadingZerosL src));
|
|
effect(TEMP dst, TEMP tmp, KILL cr);
|
|
|
|
// x |= (x >> 1);
|
|
// x |= (x >> 2);
|
|
// x |= (x >> 4);
|
|
// x |= (x >> 8);
|
|
// x |= (x >> 16);
|
|
// x |= (x >> 32);
|
|
// return (WORDBITS - popc(x));
|
|
format %{ "SRLX $src,1,$tmp\t! count leading zeros (long)\n\t"
|
|
"OR $src,$tmp,$dst\n\t"
|
|
"SRLX $dst,2,$tmp\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"SRLX $dst,4,$tmp\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"SRLX $dst,8,$tmp\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"SRLX $dst,16,$tmp\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"SRLX $dst,32,$tmp\n\t"
|
|
"OR $dst,$tmp,$dst\n\t"
|
|
"POPC $dst,$dst\n\t"
|
|
"MOV 64,$tmp\n\t"
|
|
"SUB $tmp,$dst,$dst" %}
|
|
ins_encode %{
|
|
Register Rdst = $dst$$Register;
|
|
Register Rsrc = $src$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
__ srlx(Rsrc, 1, Rtmp);
|
|
__ or3( Rsrc, Rtmp, Rdst);
|
|
__ srlx(Rdst, 2, Rtmp);
|
|
__ or3( Rdst, Rtmp, Rdst);
|
|
__ srlx(Rdst, 4, Rtmp);
|
|
__ or3( Rdst, Rtmp, Rdst);
|
|
__ srlx(Rdst, 8, Rtmp);
|
|
__ or3( Rdst, Rtmp, Rdst);
|
|
__ srlx(Rdst, 16, Rtmp);
|
|
__ or3( Rdst, Rtmp, Rdst);
|
|
__ srlx(Rdst, 32, Rtmp);
|
|
__ or3( Rdst, Rtmp, Rdst);
|
|
__ popc(Rdst, Rdst);
|
|
__ mov(BitsPerLong, Rtmp);
|
|
__ sub(Rtmp, Rdst, Rdst);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct countTrailingZerosI(iRegIsafe dst, iRegI src, flagsReg cr) %{
|
|
predicate(UsePopCountInstruction); // See Matcher::match_rule_supported
|
|
match(Set dst (CountTrailingZerosI src));
|
|
effect(TEMP dst, KILL cr);
|
|
|
|
// return popc(~x & (x - 1));
|
|
format %{ "SUB $src,1,$dst\t! count trailing zeros (int)\n\t"
|
|
"ANDN $dst,$src,$dst\n\t"
|
|
"SRL $dst,R_G0,$dst\n\t"
|
|
"POPC $dst,$dst" %}
|
|
ins_encode %{
|
|
Register Rdst = $dst$$Register;
|
|
Register Rsrc = $src$$Register;
|
|
__ sub(Rsrc, 1, Rdst);
|
|
__ andn(Rdst, Rsrc, Rdst);
|
|
__ srl(Rdst, G0, Rdst);
|
|
__ popc(Rdst, Rdst);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
instruct countTrailingZerosL(iRegIsafe dst, iRegL src, flagsReg cr) %{
|
|
predicate(UsePopCountInstruction); // See Matcher::match_rule_supported
|
|
match(Set dst (CountTrailingZerosL src));
|
|
effect(TEMP dst, KILL cr);
|
|
|
|
// return popc(~x & (x - 1));
|
|
format %{ "SUB $src,1,$dst\t! count trailing zeros (long)\n\t"
|
|
"ANDN $dst,$src,$dst\n\t"
|
|
"POPC $dst,$dst" %}
|
|
ins_encode %{
|
|
Register Rdst = $dst$$Register;
|
|
Register Rsrc = $src$$Register;
|
|
__ sub(Rsrc, 1, Rdst);
|
|
__ andn(Rdst, Rsrc, Rdst);
|
|
__ popc(Rdst, Rdst);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
|
|
//---------- Population Count Instructions -------------------------------------
|
|
|
|
instruct popCountI(iRegIsafe dst, iRegI src) %{
|
|
predicate(UsePopCountInstruction);
|
|
match(Set dst (PopCountI src));
|
|
|
|
format %{ "SRL $src, G0, $dst\t! clear upper word for 64 bit POPC\n\t"
|
|
"POPC $dst, $dst" %}
|
|
ins_encode %{
|
|
__ srl($src$$Register, G0, $dst$$Register);
|
|
__ popc($dst$$Register, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Note: Long.bitCount(long) returns an int.
|
|
instruct popCountL(iRegIsafe dst, iRegL src) %{
|
|
predicate(UsePopCountInstruction);
|
|
match(Set dst (PopCountL src));
|
|
|
|
format %{ "POPC $src, $dst" %}
|
|
ins_encode %{
|
|
__ popc($src$$Register, $dst$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
|
|
// ============================================================================
|
|
//------------Bytes reverse--------------------------------------------------
|
|
|
|
instruct bytes_reverse_int(iRegI dst, stackSlotI src) %{
|
|
match(Set dst (ReverseBytesI src));
|
|
|
|
// Op cost is artificially doubled to make sure that load or store
|
|
// instructions are preferred over this one which requires a spill
|
|
// onto a stack slot.
|
|
ins_cost(2*DEFAULT_COST + MEMORY_REF_COST);
|
|
format %{ "LDUWA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ set($src$$disp + STACK_BIAS, O7);
|
|
__ lduwa($src$$base$$Register, O7, Assembler::ASI_PRIMARY_LITTLE, $dst$$Register);
|
|
%}
|
|
ins_pipe( iload_mem );
|
|
%}
|
|
|
|
instruct bytes_reverse_long(iRegL dst, stackSlotL src) %{
|
|
match(Set dst (ReverseBytesL src));
|
|
|
|
// Op cost is artificially doubled to make sure that load or store
|
|
// instructions are preferred over this one which requires a spill
|
|
// onto a stack slot.
|
|
ins_cost(2*DEFAULT_COST + MEMORY_REF_COST);
|
|
format %{ "LDXA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ set($src$$disp + STACK_BIAS, O7);
|
|
__ ldxa($src$$base$$Register, O7, Assembler::ASI_PRIMARY_LITTLE, $dst$$Register);
|
|
%}
|
|
ins_pipe( iload_mem );
|
|
%}
|
|
|
|
instruct bytes_reverse_unsigned_short(iRegI dst, stackSlotI src) %{
|
|
match(Set dst (ReverseBytesUS src));
|
|
|
|
// Op cost is artificially doubled to make sure that load or store
|
|
// instructions are preferred over this one which requires a spill
|
|
// onto a stack slot.
|
|
ins_cost(2*DEFAULT_COST + MEMORY_REF_COST);
|
|
format %{ "LDUHA $src, $dst\t!asi=primary_little\n\t" %}
|
|
|
|
ins_encode %{
|
|
// the value was spilled as an int so bias the load
|
|
__ set($src$$disp + STACK_BIAS + 2, O7);
|
|
__ lduha($src$$base$$Register, O7, Assembler::ASI_PRIMARY_LITTLE, $dst$$Register);
|
|
%}
|
|
ins_pipe( iload_mem );
|
|
%}
|
|
|
|
instruct bytes_reverse_short(iRegI dst, stackSlotI src) %{
|
|
match(Set dst (ReverseBytesS src));
|
|
|
|
// Op cost is artificially doubled to make sure that load or store
|
|
// instructions are preferred over this one which requires a spill
|
|
// onto a stack slot.
|
|
ins_cost(2*DEFAULT_COST + MEMORY_REF_COST);
|
|
format %{ "LDSHA $src, $dst\t!asi=primary_little\n\t" %}
|
|
|
|
ins_encode %{
|
|
// the value was spilled as an int so bias the load
|
|
__ set($src$$disp + STACK_BIAS + 2, O7);
|
|
__ ldsha($src$$base$$Register, O7, Assembler::ASI_PRIMARY_LITTLE, $dst$$Register);
|
|
%}
|
|
ins_pipe( iload_mem );
|
|
%}
|
|
|
|
// Load Integer reversed byte order
|
|
instruct loadI_reversed(iRegI dst, indIndexMemory src) %{
|
|
match(Set dst (ReverseBytesI (LoadI src)));
|
|
|
|
ins_cost(DEFAULT_COST + MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "LDUWA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ lduwa($src$$base$$Register, $src$$index$$Register, Assembler::ASI_PRIMARY_LITTLE, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load Long - aligned and reversed
|
|
instruct loadL_reversed(iRegL dst, indIndexMemory src) %{
|
|
match(Set dst (ReverseBytesL (LoadL src)));
|
|
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "LDXA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ ldxa($src$$base$$Register, $src$$index$$Register, Assembler::ASI_PRIMARY_LITTLE, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load unsigned short / char reversed byte order
|
|
instruct loadUS_reversed(iRegI dst, indIndexMemory src) %{
|
|
match(Set dst (ReverseBytesUS (LoadUS src)));
|
|
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "LDUHA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ lduha($src$$base$$Register, $src$$index$$Register, Assembler::ASI_PRIMARY_LITTLE, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Load short reversed byte order
|
|
instruct loadS_reversed(iRegI dst, indIndexMemory src) %{
|
|
match(Set dst (ReverseBytesS (LoadS src)));
|
|
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "LDSHA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ ldsha($src$$base$$Register, $src$$index$$Register, Assembler::ASI_PRIMARY_LITTLE, $dst$$Register);
|
|
%}
|
|
ins_pipe(iload_mem);
|
|
%}
|
|
|
|
// Store Integer reversed byte order
|
|
instruct storeI_reversed(indIndexMemory dst, iRegI src) %{
|
|
match(Set dst (StoreI dst (ReverseBytesI src)));
|
|
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "STWA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ stwa($src$$Register, $dst$$base$$Register, $dst$$index$$Register, Assembler::ASI_PRIMARY_LITTLE);
|
|
%}
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
// Store Long reversed byte order
|
|
instruct storeL_reversed(indIndexMemory dst, iRegL src) %{
|
|
match(Set dst (StoreL dst (ReverseBytesL src)));
|
|
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "STXA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ stxa($src$$Register, $dst$$base$$Register, $dst$$index$$Register, Assembler::ASI_PRIMARY_LITTLE);
|
|
%}
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
// Store unsighed short/char reversed byte order
|
|
instruct storeUS_reversed(indIndexMemory dst, iRegI src) %{
|
|
match(Set dst (StoreC dst (ReverseBytesUS src)));
|
|
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "STHA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ stha($src$$Register, $dst$$base$$Register, $dst$$index$$Register, Assembler::ASI_PRIMARY_LITTLE);
|
|
%}
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
// Store short reversed byte order
|
|
instruct storeS_reversed(indIndexMemory dst, iRegI src) %{
|
|
match(Set dst (StoreC dst (ReverseBytesS src)));
|
|
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "STHA $src, $dst\t!asi=primary_little" %}
|
|
|
|
ins_encode %{
|
|
__ stha($src$$Register, $dst$$base$$Register, $dst$$index$$Register, Assembler::ASI_PRIMARY_LITTLE);
|
|
%}
|
|
ins_pipe(istore_mem_reg);
|
|
%}
|
|
|
|
// ====================VECTOR INSTRUCTIONS=====================================
|
|
|
|
// Load Aligned Packed values into a Double Register
|
|
instruct loadV8(regD dst, memory mem) %{
|
|
predicate(n->as_LoadVector()->memory_size() == 8);
|
|
match(Set dst (LoadVector mem));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "LDDF $mem,$dst\t! load vector (8 bytes)" %}
|
|
ins_encode %{
|
|
__ ldf(FloatRegisterImpl::D, $mem$$Address, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(floadD_mem);
|
|
%}
|
|
|
|
// Store Vector in Double register to memory
|
|
instruct storeV8(memory mem, regD src) %{
|
|
predicate(n->as_StoreVector()->memory_size() == 8);
|
|
match(Set mem (StoreVector mem src));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "STDF $src,$mem\t! store vector (8 bytes)" %}
|
|
ins_encode %{
|
|
__ stf(FloatRegisterImpl::D, as_DoubleFloatRegister($src$$reg), $mem$$Address);
|
|
%}
|
|
ins_pipe(fstoreD_mem_reg);
|
|
%}
|
|
|
|
// Store Zero into vector in memory
|
|
instruct storeV8B_zero(memory mem, immI0 zero) %{
|
|
predicate(n->as_StoreVector()->memory_size() == 8);
|
|
match(Set mem (StoreVector mem (ReplicateB zero)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "STX $zero,$mem\t! store zero vector (8 bytes)" %}
|
|
ins_encode %{
|
|
__ stx(G0, $mem$$Address);
|
|
%}
|
|
ins_pipe(fstoreD_mem_zero);
|
|
%}
|
|
|
|
instruct storeV4S_zero(memory mem, immI0 zero) %{
|
|
predicate(n->as_StoreVector()->memory_size() == 8);
|
|
match(Set mem (StoreVector mem (ReplicateS zero)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "STX $zero,$mem\t! store zero vector (4 shorts)" %}
|
|
ins_encode %{
|
|
__ stx(G0, $mem$$Address);
|
|
%}
|
|
ins_pipe(fstoreD_mem_zero);
|
|
%}
|
|
|
|
instruct storeV2I_zero(memory mem, immI0 zero) %{
|
|
predicate(n->as_StoreVector()->memory_size() == 8);
|
|
match(Set mem (StoreVector mem (ReplicateI zero)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "STX $zero,$mem\t! store zero vector (2 ints)" %}
|
|
ins_encode %{
|
|
__ stx(G0, $mem$$Address);
|
|
%}
|
|
ins_pipe(fstoreD_mem_zero);
|
|
%}
|
|
|
|
instruct storeV2F_zero(memory mem, immF0 zero) %{
|
|
predicate(n->as_StoreVector()->memory_size() == 8);
|
|
match(Set mem (StoreVector mem (ReplicateF zero)));
|
|
ins_cost(MEMORY_REF_COST);
|
|
size(4);
|
|
format %{ "STX $zero,$mem\t! store zero vector (2 floats)" %}
|
|
ins_encode %{
|
|
__ stx(G0, $mem$$Address);
|
|
%}
|
|
ins_pipe(fstoreD_mem_zero);
|
|
%}
|
|
|
|
// Replicate scalar to packed byte values into Double register
|
|
instruct Repl8B_reg(regD dst, iRegI src, iRegL tmp, o7RegL tmp2) %{
|
|
predicate(n->as_Vector()->length() == 8 && UseVIS >= 3);
|
|
match(Set dst (ReplicateB src));
|
|
effect(DEF dst, USE src, TEMP tmp, KILL tmp2);
|
|
format %{ "SLLX $src,56,$tmp\n\t"
|
|
"SRLX $tmp, 8,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\n\t"
|
|
"SRLX $tmp,16,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\n\t"
|
|
"SRLX $tmp,32,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\t! replicate8B\n\t"
|
|
"MOVXTOD $tmp,$dst\t! MoveL2D" %}
|
|
ins_encode %{
|
|
Register Rsrc = $src$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
Register Rtmp2 = $tmp2$$Register;
|
|
__ sllx(Rsrc, 56, Rtmp);
|
|
__ srlx(Rtmp, 8, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ srlx(Rtmp, 16, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ srlx(Rtmp, 32, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ movxtod(Rtmp, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Replicate scalar to packed byte values into Double stack
|
|
instruct Repl8B_stk(stackSlotD dst, iRegI src, iRegL tmp, o7RegL tmp2) %{
|
|
predicate(n->as_Vector()->length() == 8 && UseVIS < 3);
|
|
match(Set dst (ReplicateB src));
|
|
effect(DEF dst, USE src, TEMP tmp, KILL tmp2);
|
|
format %{ "SLLX $src,56,$tmp\n\t"
|
|
"SRLX $tmp, 8,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\n\t"
|
|
"SRLX $tmp,16,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\n\t"
|
|
"SRLX $tmp,32,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\t! replicate8B\n\t"
|
|
"STX $tmp,$dst\t! regL to stkD" %}
|
|
ins_encode %{
|
|
Register Rsrc = $src$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
Register Rtmp2 = $tmp2$$Register;
|
|
__ sllx(Rsrc, 56, Rtmp);
|
|
__ srlx(Rtmp, 8, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ srlx(Rtmp, 16, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ srlx(Rtmp, 32, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ set ($dst$$disp + STACK_BIAS, Rtmp2);
|
|
__ stx (Rtmp, Rtmp2, $dst$$base$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Replicate scalar constant to packed byte values in Double register
|
|
instruct Repl8B_immI(regD dst, immI13 con, o7RegI tmp) %{
|
|
predicate(n->as_Vector()->length() == 8);
|
|
match(Set dst (ReplicateB con));
|
|
effect(KILL tmp);
|
|
format %{ "LDDF [$constanttablebase + $constantoffset],$dst\t! load from constant table: Repl8B($con)" %}
|
|
ins_encode %{
|
|
// XXX This is a quick fix for 6833573.
|
|
//__ ldf(FloatRegisterImpl::D, $constanttablebase, $constantoffset(replicate_immI($con$$constant, 8, 1)), $dst$$FloatRegister);
|
|
RegisterOrConstant con_offset = __ ensure_simm13_or_reg($constantoffset(replicate_immI($con$$constant, 8, 1)), $tmp$$Register);
|
|
__ ldf(FloatRegisterImpl::D, $constanttablebase, con_offset, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(loadConFD);
|
|
%}
|
|
|
|
// Replicate scalar to packed char/short values into Double register
|
|
instruct Repl4S_reg(regD dst, iRegI src, iRegL tmp, o7RegL tmp2) %{
|
|
predicate(n->as_Vector()->length() == 4 && UseVIS >= 3);
|
|
match(Set dst (ReplicateS src));
|
|
effect(DEF dst, USE src, TEMP tmp, KILL tmp2);
|
|
format %{ "SLLX $src,48,$tmp\n\t"
|
|
"SRLX $tmp,16,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\n\t"
|
|
"SRLX $tmp,32,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\t! replicate4S\n\t"
|
|
"MOVXTOD $tmp,$dst\t! MoveL2D" %}
|
|
ins_encode %{
|
|
Register Rsrc = $src$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
Register Rtmp2 = $tmp2$$Register;
|
|
__ sllx(Rsrc, 48, Rtmp);
|
|
__ srlx(Rtmp, 16, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ srlx(Rtmp, 32, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ movxtod(Rtmp, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Replicate scalar to packed char/short values into Double stack
|
|
instruct Repl4S_stk(stackSlotD dst, iRegI src, iRegL tmp, o7RegL tmp2) %{
|
|
predicate(n->as_Vector()->length() == 4 && UseVIS < 3);
|
|
match(Set dst (ReplicateS src));
|
|
effect(DEF dst, USE src, TEMP tmp, KILL tmp2);
|
|
format %{ "SLLX $src,48,$tmp\n\t"
|
|
"SRLX $tmp,16,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\n\t"
|
|
"SRLX $tmp,32,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\t! replicate4S\n\t"
|
|
"STX $tmp,$dst\t! regL to stkD" %}
|
|
ins_encode %{
|
|
Register Rsrc = $src$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
Register Rtmp2 = $tmp2$$Register;
|
|
__ sllx(Rsrc, 48, Rtmp);
|
|
__ srlx(Rtmp, 16, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ srlx(Rtmp, 32, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ set ($dst$$disp + STACK_BIAS, Rtmp2);
|
|
__ stx (Rtmp, Rtmp2, $dst$$base$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Replicate scalar constant to packed char/short values in Double register
|
|
instruct Repl4S_immI(regD dst, immI con, o7RegI tmp) %{
|
|
predicate(n->as_Vector()->length() == 4);
|
|
match(Set dst (ReplicateS con));
|
|
effect(KILL tmp);
|
|
format %{ "LDDF [$constanttablebase + $constantoffset],$dst\t! load from constant table: Repl4S($con)" %}
|
|
ins_encode %{
|
|
// XXX This is a quick fix for 6833573.
|
|
//__ ldf(FloatRegisterImpl::D, $constanttablebase, $constantoffset(replicate_immI($con$$constant, 4, 2)), $dst$$FloatRegister);
|
|
RegisterOrConstant con_offset = __ ensure_simm13_or_reg($constantoffset(replicate_immI($con$$constant, 4, 2)), $tmp$$Register);
|
|
__ ldf(FloatRegisterImpl::D, $constanttablebase, con_offset, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(loadConFD);
|
|
%}
|
|
|
|
// Replicate scalar to packed int values into Double register
|
|
instruct Repl2I_reg(regD dst, iRegI src, iRegL tmp, o7RegL tmp2) %{
|
|
predicate(n->as_Vector()->length() == 2 && UseVIS >= 3);
|
|
match(Set dst (ReplicateI src));
|
|
effect(DEF dst, USE src, TEMP tmp, KILL tmp2);
|
|
format %{ "SLLX $src,32,$tmp\n\t"
|
|
"SRLX $tmp,32,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\t! replicate2I\n\t"
|
|
"MOVXTOD $tmp,$dst\t! MoveL2D" %}
|
|
ins_encode %{
|
|
Register Rsrc = $src$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
Register Rtmp2 = $tmp2$$Register;
|
|
__ sllx(Rsrc, 32, Rtmp);
|
|
__ srlx(Rtmp, 32, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ movxtod(Rtmp, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Replicate scalar to packed int values into Double stack
|
|
instruct Repl2I_stk(stackSlotD dst, iRegI src, iRegL tmp, o7RegL tmp2) %{
|
|
predicate(n->as_Vector()->length() == 2 && UseVIS < 3);
|
|
match(Set dst (ReplicateI src));
|
|
effect(DEF dst, USE src, TEMP tmp, KILL tmp2);
|
|
format %{ "SLLX $src,32,$tmp\n\t"
|
|
"SRLX $tmp,32,$tmp2\n\t"
|
|
"OR $tmp,$tmp2,$tmp\t! replicate2I\n\t"
|
|
"STX $tmp,$dst\t! regL to stkD" %}
|
|
ins_encode %{
|
|
Register Rsrc = $src$$Register;
|
|
Register Rtmp = $tmp$$Register;
|
|
Register Rtmp2 = $tmp2$$Register;
|
|
__ sllx(Rsrc, 32, Rtmp);
|
|
__ srlx(Rtmp, 32, Rtmp2);
|
|
__ or3 (Rtmp, Rtmp2, Rtmp);
|
|
__ set ($dst$$disp + STACK_BIAS, Rtmp2);
|
|
__ stx (Rtmp, Rtmp2, $dst$$base$$Register);
|
|
%}
|
|
ins_pipe(ialu_reg);
|
|
%}
|
|
|
|
// Replicate scalar zero constant to packed int values in Double register
|
|
instruct Repl2I_immI(regD dst, immI con, o7RegI tmp) %{
|
|
predicate(n->as_Vector()->length() == 2);
|
|
match(Set dst (ReplicateI con));
|
|
effect(KILL tmp);
|
|
format %{ "LDDF [$constanttablebase + $constantoffset],$dst\t! load from constant table: Repl2I($con)" %}
|
|
ins_encode %{
|
|
// XXX This is a quick fix for 6833573.
|
|
//__ ldf(FloatRegisterImpl::D, $constanttablebase, $constantoffset(replicate_immI($con$$constant, 2, 4)), $dst$$FloatRegister);
|
|
RegisterOrConstant con_offset = __ ensure_simm13_or_reg($constantoffset(replicate_immI($con$$constant, 2, 4)), $tmp$$Register);
|
|
__ ldf(FloatRegisterImpl::D, $constanttablebase, con_offset, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(loadConFD);
|
|
%}
|
|
|
|
// Replicate scalar to packed float values into Double stack
|
|
instruct Repl2F_stk(stackSlotD dst, regF src) %{
|
|
predicate(n->as_Vector()->length() == 2);
|
|
match(Set dst (ReplicateF src));
|
|
ins_cost(MEMORY_REF_COST*2);
|
|
format %{ "STF $src,$dst.hi\t! packed2F\n\t"
|
|
"STF $src,$dst.lo" %}
|
|
opcode(Assembler::stf_op3);
|
|
ins_encode(simple_form3_mem_reg(dst, src), form3_mem_plus_4_reg(dst, src));
|
|
ins_pipe(fstoreF_stk_reg);
|
|
%}
|
|
|
|
// Replicate scalar zero constant to packed float values in Double register
|
|
instruct Repl2F_immF(regD dst, immF con, o7RegI tmp) %{
|
|
predicate(n->as_Vector()->length() == 2);
|
|
match(Set dst (ReplicateF con));
|
|
effect(KILL tmp);
|
|
format %{ "LDDF [$constanttablebase + $constantoffset],$dst\t! load from constant table: Repl2F($con)" %}
|
|
ins_encode %{
|
|
// XXX This is a quick fix for 6833573.
|
|
//__ ldf(FloatRegisterImpl::D, $constanttablebase, $constantoffset(replicate_immF($con$$constant)), $dst$$FloatRegister);
|
|
RegisterOrConstant con_offset = __ ensure_simm13_or_reg($constantoffset(replicate_immF($con$$constant)), $tmp$$Register);
|
|
__ ldf(FloatRegisterImpl::D, $constanttablebase, con_offset, as_DoubleFloatRegister($dst$$reg));
|
|
%}
|
|
ins_pipe(loadConFD);
|
|
%}
|
|
|
|
//----------PEEPHOLE RULES-----------------------------------------------------
|
|
// These must follow all instruction definitions as they use the names
|
|
// defined in the instructions definitions.
|
|
//
|
|
// peepmatch ( root_instr_name [preceding_instruction]* );
|
|
//
|
|
// peepconstraint %{
|
|
// (instruction_number.operand_name relational_op instruction_number.operand_name
|
|
// [, ...] );
|
|
// // instruction numbers are zero-based using left to right order in peepmatch
|
|
//
|
|
// peepreplace ( instr_name ( [instruction_number.operand_name]* ) );
|
|
// // provide an instruction_number.operand_name for each operand that appears
|
|
// // in the replacement instruction's match rule
|
|
//
|
|
// ---------VM FLAGS---------------------------------------------------------
|
|
//
|
|
// All peephole optimizations can be turned off using -XX:-OptoPeephole
|
|
//
|
|
// Each peephole rule is given an identifying number starting with zero and
|
|
// increasing by one in the order seen by the parser. An individual peephole
|
|
// can be enabled, and all others disabled, by using -XX:OptoPeepholeAt=#
|
|
// on the command-line.
|
|
//
|
|
// ---------CURRENT LIMITATIONS----------------------------------------------
|
|
//
|
|
// Only match adjacent instructions in same basic block
|
|
// Only equality constraints
|
|
// Only constraints between operands, not (0.dest_reg == EAX_enc)
|
|
// Only one replacement instruction
|
|
//
|
|
// ---------EXAMPLE----------------------------------------------------------
|
|
//
|
|
// // pertinent parts of existing instructions in architecture description
|
|
// instruct movI(eRegI dst, eRegI src) %{
|
|
// match(Set dst (CopyI src));
|
|
// %}
|
|
//
|
|
// instruct incI_eReg(eRegI dst, immI1 src, eFlagsReg cr) %{
|
|
// match(Set dst (AddI dst src));
|
|
// effect(KILL cr);
|
|
// %}
|
|
//
|
|
// // Change (inc mov) to lea
|
|
// peephole %{
|
|
// // increment preceeded by register-register move
|
|
// peepmatch ( incI_eReg movI );
|
|
// // require that the destination register of the increment
|
|
// // match the destination register of the move
|
|
// peepconstraint ( 0.dst == 1.dst );
|
|
// // construct a replacement instruction that sets
|
|
// // the destination to ( move's source register + one )
|
|
// peepreplace ( incI_eReg_immI1( 0.dst 1.src 0.src ) );
|
|
// %}
|
|
//
|
|
|
|
// // Change load of spilled value to only a spill
|
|
// instruct storeI(memory mem, eRegI src) %{
|
|
// match(Set mem (StoreI mem src));
|
|
// %}
|
|
//
|
|
// instruct loadI(eRegI dst, memory mem) %{
|
|
// match(Set dst (LoadI mem));
|
|
// %}
|
|
//
|
|
// peephole %{
|
|
// peepmatch ( loadI storeI );
|
|
// peepconstraint ( 1.src == 0.dst, 1.mem == 0.mem );
|
|
// peepreplace ( storeI( 1.mem 1.mem 1.src ) );
|
|
// %}
|
|
|
|
//----------SMARTSPILL RULES---------------------------------------------------
|
|
// These must follow all instruction definitions as they use the names
|
|
// defined in the instructions definitions.
|
|
//
|
|
// SPARC will probably not have any of these rules due to RISC instruction set.
|
|
|
|
//----------PIPELINE-----------------------------------------------------------
|
|
// Rules which define the behavior of the target architectures pipeline.
|