| // |
| // Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. |
| // Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved. |
| // Copyright (c) 2020, 2023, Huawei Technologies Co., Ltd. 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. |
| // |
| // |
| |
| // RISCV 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 |
| // architecture. |
| |
| register %{ |
| //----------Architecture Description Register Definitions---------------------- |
| // General Registers |
| // "reg_def" name ( register save type, C convention save type, |
| // ideal register type, encoding ); |
| // 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. |
| |
| // We must define the 64 bit int registers in two 32 bit halves, the |
| // real lower register and a virtual upper half register. upper halves |
| // are used by the register allocator but are not actually supplied as |
| // operands to memory ops. |
| // |
| // follow the C1 compiler in making registers |
| // |
| // x7, x9-x17, x27-x31 volatile (caller save) |
| // x0-x4, x8, x23 system (no save, no allocate) |
| // x5-x6 non-allocatable (so we can use them as temporary regs) |
| |
| // |
| // as regards Java usage. we don't use any callee save registers |
| // because this makes it difficult to de-optimise a frame (see comment |
| // in x86 implementation of Deoptimization::unwind_callee_save_values) |
| // |
| |
| // General Registers |
| |
| reg_def R0 ( NS, NS, Op_RegI, 0, x0->as_VMReg() ); // zr |
| reg_def R0_H ( NS, NS, Op_RegI, 0, x0->as_VMReg()->next() ); |
| reg_def R1 ( NS, SOC, Op_RegI, 1, x1->as_VMReg() ); // ra |
| reg_def R1_H ( NS, SOC, Op_RegI, 1, x1->as_VMReg()->next() ); |
| reg_def R2 ( NS, SOE, Op_RegI, 2, x2->as_VMReg() ); // sp |
| reg_def R2_H ( NS, SOE, Op_RegI, 2, x2->as_VMReg()->next() ); |
| reg_def R3 ( NS, NS, Op_RegI, 3, x3->as_VMReg() ); // gp |
| reg_def R3_H ( NS, NS, Op_RegI, 3, x3->as_VMReg()->next() ); |
| reg_def R4 ( NS, NS, Op_RegI, 4, x4->as_VMReg() ); // tp |
| reg_def R4_H ( NS, NS, Op_RegI, 4, x4->as_VMReg()->next() ); |
| reg_def R7 ( SOC, SOC, Op_RegI, 7, x7->as_VMReg() ); |
| reg_def R7_H ( SOC, SOC, Op_RegI, 7, x7->as_VMReg()->next() ); |
| reg_def R8 ( NS, SOE, Op_RegI, 8, x8->as_VMReg() ); // fp |
| reg_def R8_H ( NS, SOE, Op_RegI, 8, x8->as_VMReg()->next() ); |
| reg_def R9 ( SOC, SOE, Op_RegI, 9, x9->as_VMReg() ); |
| reg_def R9_H ( SOC, SOE, Op_RegI, 9, x9->as_VMReg()->next() ); |
| reg_def R10 ( SOC, SOC, Op_RegI, 10, x10->as_VMReg() ); |
| reg_def R10_H ( SOC, SOC, Op_RegI, 10, x10->as_VMReg()->next()); |
| reg_def R11 ( SOC, SOC, Op_RegI, 11, x11->as_VMReg() ); |
| reg_def R11_H ( SOC, SOC, Op_RegI, 11, x11->as_VMReg()->next()); |
| reg_def R12 ( SOC, SOC, Op_RegI, 12, x12->as_VMReg() ); |
| reg_def R12_H ( SOC, SOC, Op_RegI, 12, x12->as_VMReg()->next()); |
| reg_def R13 ( SOC, SOC, Op_RegI, 13, x13->as_VMReg() ); |
| reg_def R13_H ( SOC, SOC, Op_RegI, 13, x13->as_VMReg()->next()); |
| reg_def R14 ( SOC, SOC, Op_RegI, 14, x14->as_VMReg() ); |
| reg_def R14_H ( SOC, SOC, Op_RegI, 14, x14->as_VMReg()->next()); |
| reg_def R15 ( SOC, SOC, Op_RegI, 15, x15->as_VMReg() ); |
| reg_def R15_H ( SOC, SOC, Op_RegI, 15, x15->as_VMReg()->next()); |
| reg_def R16 ( SOC, SOC, Op_RegI, 16, x16->as_VMReg() ); |
| reg_def R16_H ( SOC, SOC, Op_RegI, 16, x16->as_VMReg()->next()); |
| reg_def R17 ( SOC, SOC, Op_RegI, 17, x17->as_VMReg() ); |
| reg_def R17_H ( SOC, SOC, Op_RegI, 17, x17->as_VMReg()->next()); |
| reg_def R18 ( SOC, SOE, Op_RegI, 18, x18->as_VMReg() ); |
| reg_def R18_H ( SOC, SOE, Op_RegI, 18, x18->as_VMReg()->next()); |
| reg_def R19 ( SOC, SOE, Op_RegI, 19, x19->as_VMReg() ); |
| reg_def R19_H ( SOC, SOE, Op_RegI, 19, x19->as_VMReg()->next()); |
| reg_def R20 ( SOC, SOE, Op_RegI, 20, x20->as_VMReg() ); // caller esp |
| reg_def R20_H ( SOC, SOE, Op_RegI, 20, x20->as_VMReg()->next()); |
| reg_def R21 ( SOC, SOE, Op_RegI, 21, x21->as_VMReg() ); |
| reg_def R21_H ( SOC, SOE, Op_RegI, 21, x21->as_VMReg()->next()); |
| reg_def R22 ( SOC, SOE, Op_RegI, 22, x22->as_VMReg() ); |
| reg_def R22_H ( SOC, SOE, Op_RegI, 22, x22->as_VMReg()->next()); |
| reg_def R23 ( NS, SOE, Op_RegI, 23, x23->as_VMReg() ); // java thread |
| reg_def R23_H ( NS, SOE, Op_RegI, 23, x23->as_VMReg()->next()); |
| reg_def R24 ( SOC, SOE, Op_RegI, 24, x24->as_VMReg() ); |
| reg_def R24_H ( SOC, SOE, Op_RegI, 24, x24->as_VMReg()->next()); |
| reg_def R25 ( SOC, SOE, Op_RegI, 25, x25->as_VMReg() ); |
| reg_def R25_H ( SOC, SOE, Op_RegI, 25, x25->as_VMReg()->next()); |
| reg_def R26 ( SOC, SOE, Op_RegI, 26, x26->as_VMReg() ); |
| reg_def R26_H ( SOC, SOE, Op_RegI, 26, x26->as_VMReg()->next()); |
| reg_def R27 ( SOC, SOE, Op_RegI, 27, x27->as_VMReg() ); // heapbase |
| reg_def R27_H ( SOC, SOE, Op_RegI, 27, x27->as_VMReg()->next()); |
| reg_def R28 ( SOC, SOC, Op_RegI, 28, x28->as_VMReg() ); |
| reg_def R28_H ( SOC, SOC, Op_RegI, 28, x28->as_VMReg()->next()); |
| reg_def R29 ( SOC, SOC, Op_RegI, 29, x29->as_VMReg() ); |
| reg_def R29_H ( SOC, SOC, Op_RegI, 29, x29->as_VMReg()->next()); |
| reg_def R30 ( SOC, SOC, Op_RegI, 30, x30->as_VMReg() ); |
| reg_def R30_H ( SOC, SOC, Op_RegI, 30, x30->as_VMReg()->next()); |
| reg_def R31 ( SOC, SOC, Op_RegI, 31, x31->as_VMReg() ); |
| reg_def R31_H ( SOC, SOC, Op_RegI, 31, x31->as_VMReg()->next()); |
| |
| // ---------------------------- |
| // Float/Double Registers |
| // ---------------------------- |
| |
| // 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. |
| |
| // RISCV has 32 floating-point registers. Each can store a single |
| // or double precision floating-point value. |
| |
| // for Java use float registers f0-f31 are always save on call whereas |
| // the platform ABI treats f8-f9 and f18-f27 as callee save). Other |
| // float registers are SOC as per the platform spec |
| |
| reg_def F0 ( SOC, SOC, Op_RegF, 0, f0->as_VMReg() ); |
| reg_def F0_H ( SOC, SOC, Op_RegF, 0, f0->as_VMReg()->next() ); |
| reg_def F1 ( SOC, SOC, Op_RegF, 1, f1->as_VMReg() ); |
| reg_def F1_H ( SOC, SOC, Op_RegF, 1, f1->as_VMReg()->next() ); |
| reg_def F2 ( SOC, SOC, Op_RegF, 2, f2->as_VMReg() ); |
| reg_def F2_H ( SOC, SOC, Op_RegF, 2, f2->as_VMReg()->next() ); |
| reg_def F3 ( SOC, SOC, Op_RegF, 3, f3->as_VMReg() ); |
| reg_def F3_H ( SOC, SOC, Op_RegF, 3, f3->as_VMReg()->next() ); |
| reg_def F4 ( SOC, SOC, Op_RegF, 4, f4->as_VMReg() ); |
| reg_def F4_H ( SOC, SOC, Op_RegF, 4, f4->as_VMReg()->next() ); |
| reg_def F5 ( SOC, SOC, Op_RegF, 5, f5->as_VMReg() ); |
| reg_def F5_H ( SOC, SOC, Op_RegF, 5, f5->as_VMReg()->next() ); |
| reg_def F6 ( SOC, SOC, Op_RegF, 6, f6->as_VMReg() ); |
| reg_def F6_H ( SOC, SOC, Op_RegF, 6, f6->as_VMReg()->next() ); |
| reg_def F7 ( SOC, SOC, Op_RegF, 7, f7->as_VMReg() ); |
| reg_def F7_H ( SOC, SOC, Op_RegF, 7, f7->as_VMReg()->next() ); |
| reg_def F8 ( SOC, SOE, Op_RegF, 8, f8->as_VMReg() ); |
| reg_def F8_H ( SOC, SOE, Op_RegF, 8, f8->as_VMReg()->next() ); |
| reg_def F9 ( SOC, SOE, Op_RegF, 9, f9->as_VMReg() ); |
| reg_def F9_H ( SOC, SOE, Op_RegF, 9, f9->as_VMReg()->next() ); |
| reg_def F10 ( SOC, SOC, Op_RegF, 10, f10->as_VMReg() ); |
| reg_def F10_H ( SOC, SOC, Op_RegF, 10, f10->as_VMReg()->next() ); |
| reg_def F11 ( SOC, SOC, Op_RegF, 11, f11->as_VMReg() ); |
| reg_def F11_H ( SOC, SOC, Op_RegF, 11, f11->as_VMReg()->next() ); |
| reg_def F12 ( SOC, SOC, Op_RegF, 12, f12->as_VMReg() ); |
| reg_def F12_H ( SOC, SOC, Op_RegF, 12, f12->as_VMReg()->next() ); |
| reg_def F13 ( SOC, SOC, Op_RegF, 13, f13->as_VMReg() ); |
| reg_def F13_H ( SOC, SOC, Op_RegF, 13, f13->as_VMReg()->next() ); |
| reg_def F14 ( SOC, SOC, Op_RegF, 14, f14->as_VMReg() ); |
| reg_def F14_H ( SOC, SOC, Op_RegF, 14, f14->as_VMReg()->next() ); |
| reg_def F15 ( SOC, SOC, Op_RegF, 15, f15->as_VMReg() ); |
| reg_def F15_H ( SOC, SOC, Op_RegF, 15, f15->as_VMReg()->next() ); |
| reg_def F16 ( SOC, SOC, Op_RegF, 16, f16->as_VMReg() ); |
| reg_def F16_H ( SOC, SOC, Op_RegF, 16, f16->as_VMReg()->next() ); |
| reg_def F17 ( SOC, SOC, Op_RegF, 17, f17->as_VMReg() ); |
| reg_def F17_H ( SOC, SOC, Op_RegF, 17, f17->as_VMReg()->next() ); |
| reg_def F18 ( SOC, SOE, Op_RegF, 18, f18->as_VMReg() ); |
| reg_def F18_H ( SOC, SOE, Op_RegF, 18, f18->as_VMReg()->next() ); |
| reg_def F19 ( SOC, SOE, Op_RegF, 19, f19->as_VMReg() ); |
| reg_def F19_H ( SOC, SOE, Op_RegF, 19, f19->as_VMReg()->next() ); |
| reg_def F20 ( SOC, SOE, Op_RegF, 20, f20->as_VMReg() ); |
| reg_def F20_H ( SOC, SOE, Op_RegF, 20, f20->as_VMReg()->next() ); |
| reg_def F21 ( SOC, SOE, Op_RegF, 21, f21->as_VMReg() ); |
| reg_def F21_H ( SOC, SOE, Op_RegF, 21, f21->as_VMReg()->next() ); |
| reg_def F22 ( SOC, SOE, Op_RegF, 22, f22->as_VMReg() ); |
| reg_def F22_H ( SOC, SOE, Op_RegF, 22, f22->as_VMReg()->next() ); |
| reg_def F23 ( SOC, SOE, Op_RegF, 23, f23->as_VMReg() ); |
| reg_def F23_H ( SOC, SOE, Op_RegF, 23, f23->as_VMReg()->next() ); |
| reg_def F24 ( SOC, SOE, Op_RegF, 24, f24->as_VMReg() ); |
| reg_def F24_H ( SOC, SOE, Op_RegF, 24, f24->as_VMReg()->next() ); |
| reg_def F25 ( SOC, SOE, Op_RegF, 25, f25->as_VMReg() ); |
| reg_def F25_H ( SOC, SOE, Op_RegF, 25, f25->as_VMReg()->next() ); |
| reg_def F26 ( SOC, SOE, Op_RegF, 26, f26->as_VMReg() ); |
| reg_def F26_H ( SOC, SOE, Op_RegF, 26, f26->as_VMReg()->next() ); |
| reg_def F27 ( SOC, SOE, Op_RegF, 27, f27->as_VMReg() ); |
| reg_def F27_H ( SOC, SOE, Op_RegF, 27, f27->as_VMReg()->next() ); |
| reg_def F28 ( SOC, SOC, Op_RegF, 28, f28->as_VMReg() ); |
| reg_def F28_H ( SOC, SOC, Op_RegF, 28, f28->as_VMReg()->next() ); |
| reg_def F29 ( SOC, SOC, Op_RegF, 29, f29->as_VMReg() ); |
| reg_def F29_H ( SOC, SOC, Op_RegF, 29, f29->as_VMReg()->next() ); |
| reg_def F30 ( SOC, SOC, Op_RegF, 30, f30->as_VMReg() ); |
| reg_def F30_H ( SOC, SOC, Op_RegF, 30, f30->as_VMReg()->next() ); |
| reg_def F31 ( SOC, SOC, Op_RegF, 31, f31->as_VMReg() ); |
| reg_def F31_H ( SOC, SOC, Op_RegF, 31, f31->as_VMReg()->next() ); |
| |
| // ---------------------------- |
| // Vector Registers |
| // ---------------------------- |
| |
| // For RVV vector registers, we simply extend vector register size to 4 |
| // 'logical' slots. This is nominally 128 bits but it actually covers |
| // all possible 'physical' RVV vector register lengths from 128 ~ 1024 |
| // bits. The 'physical' RVV vector register length is detected during |
| // startup, so the register allocator is able to identify the correct |
| // number of bytes needed for an RVV spill/unspill. |
| |
| reg_def V0 ( SOC, SOC, Op_VecA, 0, v0->as_VMReg() ); |
| reg_def V0_H ( SOC, SOC, Op_VecA, 0, v0->as_VMReg()->next() ); |
| reg_def V0_J ( SOC, SOC, Op_VecA, 0, v0->as_VMReg()->next(2) ); |
| reg_def V0_K ( SOC, SOC, Op_VecA, 0, v0->as_VMReg()->next(3) ); |
| |
| reg_def V1 ( SOC, SOC, Op_VecA, 1, v1->as_VMReg() ); |
| reg_def V1_H ( SOC, SOC, Op_VecA, 1, v1->as_VMReg()->next() ); |
| reg_def V1_J ( SOC, SOC, Op_VecA, 1, v1->as_VMReg()->next(2) ); |
| reg_def V1_K ( SOC, SOC, Op_VecA, 1, v1->as_VMReg()->next(3) ); |
| |
| reg_def V2 ( SOC, SOC, Op_VecA, 2, v2->as_VMReg() ); |
| reg_def V2_H ( SOC, SOC, Op_VecA, 2, v2->as_VMReg()->next() ); |
| reg_def V2_J ( SOC, SOC, Op_VecA, 2, v2->as_VMReg()->next(2) ); |
| reg_def V2_K ( SOC, SOC, Op_VecA, 2, v2->as_VMReg()->next(3) ); |
| |
| reg_def V3 ( SOC, SOC, Op_VecA, 3, v3->as_VMReg() ); |
| reg_def V3_H ( SOC, SOC, Op_VecA, 3, v3->as_VMReg()->next() ); |
| reg_def V3_J ( SOC, SOC, Op_VecA, 3, v3->as_VMReg()->next(2) ); |
| reg_def V3_K ( SOC, SOC, Op_VecA, 3, v3->as_VMReg()->next(3) ); |
| |
| reg_def V4 ( SOC, SOC, Op_VecA, 4, v4->as_VMReg() ); |
| reg_def V4_H ( SOC, SOC, Op_VecA, 4, v4->as_VMReg()->next() ); |
| reg_def V4_J ( SOC, SOC, Op_VecA, 4, v4->as_VMReg()->next(2) ); |
| reg_def V4_K ( SOC, SOC, Op_VecA, 4, v4->as_VMReg()->next(3) ); |
| |
| reg_def V5 ( SOC, SOC, Op_VecA, 5, v5->as_VMReg() ); |
| reg_def V5_H ( SOC, SOC, Op_VecA, 5, v5->as_VMReg()->next() ); |
| reg_def V5_J ( SOC, SOC, Op_VecA, 5, v5->as_VMReg()->next(2) ); |
| reg_def V5_K ( SOC, SOC, Op_VecA, 5, v5->as_VMReg()->next(3) ); |
| |
| reg_def V6 ( SOC, SOC, Op_VecA, 6, v6->as_VMReg() ); |
| reg_def V6_H ( SOC, SOC, Op_VecA, 6, v6->as_VMReg()->next() ); |
| reg_def V6_J ( SOC, SOC, Op_VecA, 6, v6->as_VMReg()->next(2) ); |
| reg_def V6_K ( SOC, SOC, Op_VecA, 6, v6->as_VMReg()->next(3) ); |
| |
| reg_def V7 ( SOC, SOC, Op_VecA, 7, v7->as_VMReg() ); |
| reg_def V7_H ( SOC, SOC, Op_VecA, 7, v7->as_VMReg()->next() ); |
| reg_def V7_J ( SOC, SOC, Op_VecA, 7, v7->as_VMReg()->next(2) ); |
| reg_def V7_K ( SOC, SOC, Op_VecA, 7, v7->as_VMReg()->next(3) ); |
| |
| reg_def V8 ( SOC, SOC, Op_VecA, 8, v8->as_VMReg() ); |
| reg_def V8_H ( SOC, SOC, Op_VecA, 8, v8->as_VMReg()->next() ); |
| reg_def V8_J ( SOC, SOC, Op_VecA, 8, v8->as_VMReg()->next(2) ); |
| reg_def V8_K ( SOC, SOC, Op_VecA, 8, v8->as_VMReg()->next(3) ); |
| |
| reg_def V9 ( SOC, SOC, Op_VecA, 9, v9->as_VMReg() ); |
| reg_def V9_H ( SOC, SOC, Op_VecA, 9, v9->as_VMReg()->next() ); |
| reg_def V9_J ( SOC, SOC, Op_VecA, 9, v9->as_VMReg()->next(2) ); |
| reg_def V9_K ( SOC, SOC, Op_VecA, 9, v9->as_VMReg()->next(3) ); |
| |
| reg_def V10 ( SOC, SOC, Op_VecA, 10, v10->as_VMReg() ); |
| reg_def V10_H ( SOC, SOC, Op_VecA, 10, v10->as_VMReg()->next() ); |
| reg_def V10_J ( SOC, SOC, Op_VecA, 10, v10->as_VMReg()->next(2) ); |
| reg_def V10_K ( SOC, SOC, Op_VecA, 10, v10->as_VMReg()->next(3) ); |
| |
| reg_def V11 ( SOC, SOC, Op_VecA, 11, v11->as_VMReg() ); |
| reg_def V11_H ( SOC, SOC, Op_VecA, 11, v11->as_VMReg()->next() ); |
| reg_def V11_J ( SOC, SOC, Op_VecA, 11, v11->as_VMReg()->next(2) ); |
| reg_def V11_K ( SOC, SOC, Op_VecA, 11, v11->as_VMReg()->next(3) ); |
| |
| reg_def V12 ( SOC, SOC, Op_VecA, 12, v12->as_VMReg() ); |
| reg_def V12_H ( SOC, SOC, Op_VecA, 12, v12->as_VMReg()->next() ); |
| reg_def V12_J ( SOC, SOC, Op_VecA, 12, v12->as_VMReg()->next(2) ); |
| reg_def V12_K ( SOC, SOC, Op_VecA, 12, v12->as_VMReg()->next(3) ); |
| |
| reg_def V13 ( SOC, SOC, Op_VecA, 13, v13->as_VMReg() ); |
| reg_def V13_H ( SOC, SOC, Op_VecA, 13, v13->as_VMReg()->next() ); |
| reg_def V13_J ( SOC, SOC, Op_VecA, 13, v13->as_VMReg()->next(2) ); |
| reg_def V13_K ( SOC, SOC, Op_VecA, 13, v13->as_VMReg()->next(3) ); |
| |
| reg_def V14 ( SOC, SOC, Op_VecA, 14, v14->as_VMReg() ); |
| reg_def V14_H ( SOC, SOC, Op_VecA, 14, v14->as_VMReg()->next() ); |
| reg_def V14_J ( SOC, SOC, Op_VecA, 14, v14->as_VMReg()->next(2) ); |
| reg_def V14_K ( SOC, SOC, Op_VecA, 14, v14->as_VMReg()->next(3) ); |
| |
| reg_def V15 ( SOC, SOC, Op_VecA, 15, v15->as_VMReg() ); |
| reg_def V15_H ( SOC, SOC, Op_VecA, 15, v15->as_VMReg()->next() ); |
| reg_def V15_J ( SOC, SOC, Op_VecA, 15, v15->as_VMReg()->next(2) ); |
| reg_def V15_K ( SOC, SOC, Op_VecA, 15, v15->as_VMReg()->next(3) ); |
| |
| reg_def V16 ( SOC, SOC, Op_VecA, 16, v16->as_VMReg() ); |
| reg_def V16_H ( SOC, SOC, Op_VecA, 16, v16->as_VMReg()->next() ); |
| reg_def V16_J ( SOC, SOC, Op_VecA, 16, v16->as_VMReg()->next(2) ); |
| reg_def V16_K ( SOC, SOC, Op_VecA, 16, v16->as_VMReg()->next(3) ); |
| |
| reg_def V17 ( SOC, SOC, Op_VecA, 17, v17->as_VMReg() ); |
| reg_def V17_H ( SOC, SOC, Op_VecA, 17, v17->as_VMReg()->next() ); |
| reg_def V17_J ( SOC, SOC, Op_VecA, 17, v17->as_VMReg()->next(2) ); |
| reg_def V17_K ( SOC, SOC, Op_VecA, 17, v17->as_VMReg()->next(3) ); |
| |
| reg_def V18 ( SOC, SOC, Op_VecA, 18, v18->as_VMReg() ); |
| reg_def V18_H ( SOC, SOC, Op_VecA, 18, v18->as_VMReg()->next() ); |
| reg_def V18_J ( SOC, SOC, Op_VecA, 18, v18->as_VMReg()->next(2) ); |
| reg_def V18_K ( SOC, SOC, Op_VecA, 18, v18->as_VMReg()->next(3) ); |
| |
| reg_def V19 ( SOC, SOC, Op_VecA, 19, v19->as_VMReg() ); |
| reg_def V19_H ( SOC, SOC, Op_VecA, 19, v19->as_VMReg()->next() ); |
| reg_def V19_J ( SOC, SOC, Op_VecA, 19, v19->as_VMReg()->next(2) ); |
| reg_def V19_K ( SOC, SOC, Op_VecA, 19, v19->as_VMReg()->next(3) ); |
| |
| reg_def V20 ( SOC, SOC, Op_VecA, 20, v20->as_VMReg() ); |
| reg_def V20_H ( SOC, SOC, Op_VecA, 20, v20->as_VMReg()->next() ); |
| reg_def V20_J ( SOC, SOC, Op_VecA, 20, v20->as_VMReg()->next(2) ); |
| reg_def V20_K ( SOC, SOC, Op_VecA, 20, v20->as_VMReg()->next(3) ); |
| |
| reg_def V21 ( SOC, SOC, Op_VecA, 21, v21->as_VMReg() ); |
| reg_def V21_H ( SOC, SOC, Op_VecA, 21, v21->as_VMReg()->next() ); |
| reg_def V21_J ( SOC, SOC, Op_VecA, 21, v21->as_VMReg()->next(2) ); |
| reg_def V21_K ( SOC, SOC, Op_VecA, 21, v21->as_VMReg()->next(3) ); |
| |
| reg_def V22 ( SOC, SOC, Op_VecA, 22, v22->as_VMReg() ); |
| reg_def V22_H ( SOC, SOC, Op_VecA, 22, v22->as_VMReg()->next() ); |
| reg_def V22_J ( SOC, SOC, Op_VecA, 22, v22->as_VMReg()->next(2) ); |
| reg_def V22_K ( SOC, SOC, Op_VecA, 22, v22->as_VMReg()->next(3) ); |
| |
| reg_def V23 ( SOC, SOC, Op_VecA, 23, v23->as_VMReg() ); |
| reg_def V23_H ( SOC, SOC, Op_VecA, 23, v23->as_VMReg()->next() ); |
| reg_def V23_J ( SOC, SOC, Op_VecA, 23, v23->as_VMReg()->next(2) ); |
| reg_def V23_K ( SOC, SOC, Op_VecA, 23, v23->as_VMReg()->next(3) ); |
| |
| reg_def V24 ( SOC, SOC, Op_VecA, 24, v24->as_VMReg() ); |
| reg_def V24_H ( SOC, SOC, Op_VecA, 24, v24->as_VMReg()->next() ); |
| reg_def V24_J ( SOC, SOC, Op_VecA, 24, v24->as_VMReg()->next(2) ); |
| reg_def V24_K ( SOC, SOC, Op_VecA, 24, v24->as_VMReg()->next(3) ); |
| |
| reg_def V25 ( SOC, SOC, Op_VecA, 25, v25->as_VMReg() ); |
| reg_def V25_H ( SOC, SOC, Op_VecA, 25, v25->as_VMReg()->next() ); |
| reg_def V25_J ( SOC, SOC, Op_VecA, 25, v25->as_VMReg()->next(2) ); |
| reg_def V25_K ( SOC, SOC, Op_VecA, 25, v25->as_VMReg()->next(3) ); |
| |
| reg_def V26 ( SOC, SOC, Op_VecA, 26, v26->as_VMReg() ); |
| reg_def V26_H ( SOC, SOC, Op_VecA, 26, v26->as_VMReg()->next() ); |
| reg_def V26_J ( SOC, SOC, Op_VecA, 26, v26->as_VMReg()->next(2) ); |
| reg_def V26_K ( SOC, SOC, Op_VecA, 26, v26->as_VMReg()->next(3) ); |
| |
| reg_def V27 ( SOC, SOC, Op_VecA, 27, v27->as_VMReg() ); |
| reg_def V27_H ( SOC, SOC, Op_VecA, 27, v27->as_VMReg()->next() ); |
| reg_def V27_J ( SOC, SOC, Op_VecA, 27, v27->as_VMReg()->next(2) ); |
| reg_def V27_K ( SOC, SOC, Op_VecA, 27, v27->as_VMReg()->next(3) ); |
| |
| reg_def V28 ( SOC, SOC, Op_VecA, 28, v28->as_VMReg() ); |
| reg_def V28_H ( SOC, SOC, Op_VecA, 28, v28->as_VMReg()->next() ); |
| reg_def V28_J ( SOC, SOC, Op_VecA, 28, v28->as_VMReg()->next(2) ); |
| reg_def V28_K ( SOC, SOC, Op_VecA, 28, v28->as_VMReg()->next(3) ); |
| |
| reg_def V29 ( SOC, SOC, Op_VecA, 29, v29->as_VMReg() ); |
| reg_def V29_H ( SOC, SOC, Op_VecA, 29, v29->as_VMReg()->next() ); |
| reg_def V29_J ( SOC, SOC, Op_VecA, 29, v29->as_VMReg()->next(2) ); |
| reg_def V29_K ( SOC, SOC, Op_VecA, 29, v29->as_VMReg()->next(3) ); |
| |
| reg_def V30 ( SOC, SOC, Op_VecA, 30, v30->as_VMReg() ); |
| reg_def V30_H ( SOC, SOC, Op_VecA, 30, v30->as_VMReg()->next() ); |
| reg_def V30_J ( SOC, SOC, Op_VecA, 30, v30->as_VMReg()->next(2) ); |
| reg_def V30_K ( SOC, SOC, Op_VecA, 30, v30->as_VMReg()->next(3) ); |
| |
| reg_def V31 ( SOC, SOC, Op_VecA, 31, v31->as_VMReg() ); |
| reg_def V31_H ( SOC, SOC, Op_VecA, 31, v31->as_VMReg()->next() ); |
| reg_def V31_J ( SOC, SOC, Op_VecA, 31, v31->as_VMReg()->next(2) ); |
| reg_def V31_K ( SOC, SOC, Op_VecA, 31, v31->as_VMReg()->next(3) ); |
| |
| // ---------------------------- |
| // Special Registers |
| // ---------------------------- |
| |
| // On riscv, the physical flag register is missing, so we use t1 instead, |
| // to bridge the RegFlag semantics in share/opto |
| |
| reg_def RFLAGS (SOC, SOC, Op_RegFlags, 6, x6->as_VMReg() ); |
| |
| // Specify priority of register selection within phases of register |
| // allocation. Highest priority is first. A useful heuristic is to |
| // give registers a low priority when they are required by machine |
| // instructions, like EAX and EDX on I486, and choose no-save registers |
| // before save-on-call, & save-on-call before save-on-entry. Registers |
| // which participate in fixed calling sequences should come last. |
| // Registers which are used as pairs must fall on an even boundary. |
| |
| alloc_class chunk0( |
| // volatiles |
| R7, R7_H, |
| R28, R28_H, |
| R29, R29_H, |
| R30, R30_H, |
| R31, R31_H, |
| |
| // arg registers |
| R10, R10_H, |
| R11, R11_H, |
| R12, R12_H, |
| R13, R13_H, |
| R14, R14_H, |
| R15, R15_H, |
| R16, R16_H, |
| R17, R17_H, |
| |
| // non-volatiles |
| R9, R9_H, |
| R18, R18_H, |
| R19, R19_H, |
| R20, R20_H, |
| R21, R21_H, |
| R22, R22_H, |
| R24, R24_H, |
| R25, R25_H, |
| R26, R26_H, |
| |
| // non-allocatable registers |
| R23, R23_H, // java thread |
| R27, R27_H, // heapbase |
| R4, R4_H, // thread |
| R8, R8_H, // fp |
| R0, R0_H, // zero |
| R1, R1_H, // ra |
| R2, R2_H, // sp |
| R3, R3_H, // gp |
| ); |
| |
| alloc_class chunk1( |
| |
| // no save |
| F0, F0_H, |
| F1, F1_H, |
| F2, F2_H, |
| F3, F3_H, |
| F4, F4_H, |
| F5, F5_H, |
| F6, F6_H, |
| F7, F7_H, |
| F28, F28_H, |
| F29, F29_H, |
| F30, F30_H, |
| F31, F31_H, |
| |
| // arg registers |
| F10, F10_H, |
| F11, F11_H, |
| F12, F12_H, |
| F13, F13_H, |
| F14, F14_H, |
| F15, F15_H, |
| F16, F16_H, |
| F17, F17_H, |
| |
| // non-volatiles |
| F8, F8_H, |
| F9, F9_H, |
| F18, F18_H, |
| F19, F19_H, |
| F20, F20_H, |
| F21, F21_H, |
| F22, F22_H, |
| F23, F23_H, |
| F24, F24_H, |
| F25, F25_H, |
| F26, F26_H, |
| F27, F27_H, |
| ); |
| |
| alloc_class chunk2( |
| V0, V0_H, V0_J, V0_K, |
| V1, V1_H, V1_J, V1_K, |
| V2, V2_H, V2_J, V2_K, |
| V3, V3_H, V3_J, V3_K, |
| V4, V4_H, V4_J, V4_K, |
| V5, V5_H, V5_J, V5_K, |
| V6, V6_H, V6_J, V6_K, |
| V7, V7_H, V7_J, V7_K, |
| V8, V8_H, V8_J, V8_K, |
| V9, V9_H, V9_J, V9_K, |
| V10, V10_H, V10_J, V10_K, |
| V11, V11_H, V11_J, V11_K, |
| V12, V12_H, V12_J, V12_K, |
| V13, V13_H, V13_J, V13_K, |
| V14, V14_H, V14_J, V14_K, |
| V15, V15_H, V15_J, V15_K, |
| V16, V16_H, V16_J, V16_K, |
| V17, V17_H, V17_J, V17_K, |
| V18, V18_H, V18_J, V18_K, |
| V19, V19_H, V19_J, V19_K, |
| V20, V20_H, V20_J, V20_K, |
| V21, V21_H, V21_J, V21_K, |
| V22, V22_H, V22_J, V22_K, |
| V23, V23_H, V23_J, V23_K, |
| V24, V24_H, V24_J, V24_K, |
| V25, V25_H, V25_J, V25_K, |
| V26, V26_H, V26_J, V26_K, |
| V27, V27_H, V27_J, V27_K, |
| V28, V28_H, V28_J, V28_K, |
| V29, V29_H, V29_J, V29_K, |
| V30, V30_H, V30_J, V30_K, |
| V31, V31_H, V31_J, V31_K, |
| ); |
| |
| alloc_class chunk3(RFLAGS); |
| |
| //----------Architecture Description Register Classes-------------------------- |
| // Several register classes are automatically defined based upon information in |
| // this architecture description. |
| // 1) reg_class inline_cache_reg ( /* as def'd in frame section */ ) |
| // 2) reg_class stack_slots( /* one chunk of stack-based "registers" */ ) |
| // |
| |
| // Class for all 32 bit general purpose registers |
| reg_class all_reg32( |
| R0, |
| R1, |
| R2, |
| R3, |
| R4, |
| R7, |
| R8, |
| R9, |
| R10, |
| R11, |
| R12, |
| R13, |
| R14, |
| R15, |
| R16, |
| R17, |
| R18, |
| R19, |
| R20, |
| R21, |
| R22, |
| R23, |
| R24, |
| R25, |
| R26, |
| R27, |
| R28, |
| R29, |
| R30, |
| R31 |
| ); |
| |
| // Class for any 32 bit integer registers (excluding zr) |
| reg_class any_reg32 %{ |
| return _ANY_REG32_mask; |
| %} |
| |
| // Singleton class for R10 int register |
| reg_class int_r10_reg(R10); |
| |
| // Singleton class for R12 int register |
| reg_class int_r12_reg(R12); |
| |
| // Singleton class for R13 int register |
| reg_class int_r13_reg(R13); |
| |
| // Singleton class for R14 int register |
| reg_class int_r14_reg(R14); |
| |
| // Class for all long integer registers |
| reg_class all_reg( |
| R0, R0_H, |
| R1, R1_H, |
| R2, R2_H, |
| R3, R3_H, |
| R4, R4_H, |
| R7, R7_H, |
| R8, R8_H, |
| R9, R9_H, |
| R10, R10_H, |
| R11, R11_H, |
| R12, R12_H, |
| R13, R13_H, |
| R14, R14_H, |
| R15, R15_H, |
| R16, R16_H, |
| R17, R17_H, |
| R18, R18_H, |
| R19, R19_H, |
| R20, R20_H, |
| R21, R21_H, |
| R22, R22_H, |
| R23, R23_H, |
| R24, R24_H, |
| R25, R25_H, |
| R26, R26_H, |
| R27, R27_H, |
| R28, R28_H, |
| R29, R29_H, |
| R30, R30_H, |
| R31, R31_H |
| ); |
| |
| // Class for all long integer registers (excluding zr) |
| reg_class any_reg %{ |
| return _ANY_REG_mask; |
| %} |
| |
| // Class for non-allocatable 32 bit registers |
| reg_class non_allocatable_reg32( |
| R0, // zr |
| R1, // ra |
| R2, // sp |
| R3, // gp |
| R4, // tp |
| R23 // java thread |
| ); |
| |
| // Class for non-allocatable 64 bit registers |
| reg_class non_allocatable_reg( |
| R0, R0_H, // zr |
| R1, R1_H, // ra |
| R2, R2_H, // sp |
| R3, R3_H, // gp |
| R4, R4_H, // tp |
| R23, R23_H // java thread |
| ); |
| |
| reg_class no_special_reg32 %{ |
| return _NO_SPECIAL_REG32_mask; |
| %} |
| |
| reg_class no_special_reg %{ |
| return _NO_SPECIAL_REG_mask; |
| %} |
| |
| reg_class ptr_reg %{ |
| return _PTR_REG_mask; |
| %} |
| |
| reg_class no_special_ptr_reg %{ |
| return _NO_SPECIAL_PTR_REG_mask; |
| %} |
| |
| // Class for 64 bit register r10 |
| reg_class r10_reg( |
| R10, R10_H |
| ); |
| |
| // Class for 64 bit register r11 |
| reg_class r11_reg( |
| R11, R11_H |
| ); |
| |
| // Class for 64 bit register r12 |
| reg_class r12_reg( |
| R12, R12_H |
| ); |
| |
| // Class for 64 bit register r13 |
| reg_class r13_reg( |
| R13, R13_H |
| ); |
| |
| // Class for 64 bit register r14 |
| reg_class r14_reg( |
| R14, R14_H |
| ); |
| |
| // Class for 64 bit register r15 |
| reg_class r15_reg( |
| R15, R15_H |
| ); |
| |
| // Class for 64 bit register r16 |
| reg_class r16_reg( |
| R16, R16_H |
| ); |
| |
| // Class for method register |
| reg_class method_reg( |
| R31, R31_H |
| ); |
| |
| // Class for java thread register |
| reg_class java_thread_reg( |
| R23, R23_H |
| ); |
| |
| reg_class r28_reg( |
| R28, R28_H |
| ); |
| |
| reg_class r29_reg( |
| R29, R29_H |
| ); |
| |
| reg_class r30_reg( |
| R30, R30_H |
| ); |
| |
| reg_class r31_reg( |
| R31, R31_H |
| ); |
| |
| // Class for zero registesr |
| reg_class zr_reg( |
| R0, R0_H |
| ); |
| |
| // Class for thread register |
| reg_class thread_reg( |
| R4, R4_H |
| ); |
| |
| // Class for frame pointer register |
| reg_class fp_reg( |
| R8, R8_H |
| ); |
| |
| // Class for link register |
| reg_class ra_reg( |
| R1, R1_H |
| ); |
| |
| // Class for long sp register |
| reg_class sp_reg( |
| R2, R2_H |
| ); |
| |
| // Class for all float registers |
| reg_class float_reg( |
| F0, |
| F1, |
| F2, |
| F3, |
| F4, |
| F5, |
| F6, |
| F7, |
| F8, |
| F9, |
| F10, |
| F11, |
| F12, |
| F13, |
| F14, |
| F15, |
| F16, |
| F17, |
| F18, |
| F19, |
| F20, |
| F21, |
| F22, |
| F23, |
| F24, |
| F25, |
| F26, |
| F27, |
| F28, |
| F29, |
| F30, |
| F31 |
| ); |
| |
| // Double precision float registers have virtual `high halves' that |
| // are needed by the allocator. |
| // Class for all double registers |
| reg_class double_reg( |
| F0, F0_H, |
| F1, F1_H, |
| F2, F2_H, |
| F3, F3_H, |
| F4, F4_H, |
| F5, F5_H, |
| F6, F6_H, |
| F7, F7_H, |
| F8, F8_H, |
| F9, F9_H, |
| F10, F10_H, |
| F11, F11_H, |
| F12, F12_H, |
| F13, F13_H, |
| F14, F14_H, |
| F15, F15_H, |
| F16, F16_H, |
| F17, F17_H, |
| F18, F18_H, |
| F19, F19_H, |
| F20, F20_H, |
| F21, F21_H, |
| F22, F22_H, |
| F23, F23_H, |
| F24, F24_H, |
| F25, F25_H, |
| F26, F26_H, |
| F27, F27_H, |
| F28, F28_H, |
| F29, F29_H, |
| F30, F30_H, |
| F31, F31_H |
| ); |
| |
| // Class for RVV vector registers |
| // Note: v0, v30 and v31 are used as mask registers. |
| reg_class vectora_reg( |
| V1, V1_H, V1_J, V1_K, |
| V2, V2_H, V2_J, V2_K, |
| V3, V3_H, V3_J, V3_K, |
| V4, V4_H, V4_J, V4_K, |
| V5, V5_H, V5_J, V5_K, |
| V6, V6_H, V6_J, V6_K, |
| V7, V7_H, V7_J, V7_K, |
| V8, V8_H, V8_J, V8_K, |
| V9, V9_H, V9_J, V9_K, |
| V10, V10_H, V10_J, V10_K, |
| V11, V11_H, V11_J, V11_K, |
| V12, V12_H, V12_J, V12_K, |
| V13, V13_H, V13_J, V13_K, |
| V14, V14_H, V14_J, V14_K, |
| V15, V15_H, V15_J, V15_K, |
| V16, V16_H, V16_J, V16_K, |
| V17, V17_H, V17_J, V17_K, |
| V18, V18_H, V18_J, V18_K, |
| V19, V19_H, V19_J, V19_K, |
| V20, V20_H, V20_J, V20_K, |
| V21, V21_H, V21_J, V21_K, |
| V22, V22_H, V22_J, V22_K, |
| V23, V23_H, V23_J, V23_K, |
| V24, V24_H, V24_J, V24_K, |
| V25, V25_H, V25_J, V25_K, |
| V26, V26_H, V26_J, V26_K, |
| V27, V27_H, V27_J, V27_K, |
| V28, V28_H, V28_J, V28_K, |
| V29, V29_H, V29_J, V29_K |
| ); |
| |
| // Class for 64 bit register f0 |
| reg_class f0_reg( |
| F0, F0_H |
| ); |
| |
| // Class for 64 bit register f1 |
| reg_class f1_reg( |
| F1, F1_H |
| ); |
| |
| // Class for 64 bit register f2 |
| reg_class f2_reg( |
| F2, F2_H |
| ); |
| |
| // Class for 64 bit register f3 |
| reg_class f3_reg( |
| F3, F3_H |
| ); |
| |
| // class for vector register v1 |
| reg_class v1_reg( |
| V1, V1_H, V1_J, V1_K |
| ); |
| |
| // class for vector register v2 |
| reg_class v2_reg( |
| V2, V2_H, V2_J, V2_K |
| ); |
| |
| // class for vector register v3 |
| reg_class v3_reg( |
| V3, V3_H, V3_J, V3_K |
| ); |
| |
| // class for vector register v4 |
| reg_class v4_reg( |
| V4, V4_H, V4_J, V4_K |
| ); |
| |
| // class for vector register v5 |
| reg_class v5_reg( |
| V5, V5_H, V5_J, V5_K |
| ); |
| |
| // class for vector register v6 |
| reg_class v6_reg( |
| V6, V6_H, V6_J, V6_K |
| ); |
| |
| // class for vector register v7 |
| reg_class v7_reg( |
| V7, V7_H, V7_J, V7_K |
| ); |
| |
| // class for vector register v8 |
| reg_class v8_reg( |
| V8, V8_H, V8_J, V8_K |
| ); |
| |
| // class for vector register v9 |
| reg_class v9_reg( |
| V9, V9_H, V9_J, V9_K |
| ); |
| |
| // class for vector register v10 |
| reg_class v10_reg( |
| V10, V10_H, V10_J, V10_K |
| ); |
| |
| // class for vector register v11 |
| reg_class v11_reg( |
| V11, V11_H, V11_J, V11_K |
| ); |
| |
| // class for condition codes |
| reg_class reg_flags(RFLAGS); |
| |
| // Class for RVV v0 mask register |
| // https://github.com/riscv/riscv-v-spec/blob/master/v-spec.adoc#53-vector-masking |
| // The mask value used to control execution of a masked vector |
| // instruction is always supplied by vector register v0. |
| reg_class vmask_reg_v0 ( |
| V0 |
| ); |
| |
| // Class for RVV mask registers |
| // We need two more vmask registers to do the vector mask logical ops, |
| // so define v30, v31 as mask register too. |
| reg_class vmask_reg ( |
| V0, |
| V30, |
| V31 |
| ); |
| %} |
| |
| //----------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>"); |
| // |
| |
| // we follow the ppc-aix port in using a simple cost model which ranks |
| // register operations as cheap, memory ops as more expensive and |
| // branches as most expensive. the first two have a low as well as a |
| // normal cost. huge cost appears to be a way of saying don't do |
| // something |
| |
| definitions %{ |
| // The default cost (of a register move instruction). |
| int_def DEFAULT_COST ( 100, 100); |
| int_def ALU_COST ( 100, 1 * DEFAULT_COST); // unknown, const, arith, shift, slt, |
| // multi, auipc, nop, logical, move |
| int_def LOAD_COST ( 300, 3 * DEFAULT_COST); // load, fpload |
| int_def STORE_COST ( 100, 1 * DEFAULT_COST); // store, fpstore |
| int_def XFER_COST ( 300, 3 * DEFAULT_COST); // mfc, mtc, fcvt, fmove, fcmp |
| int_def FMVX_COST ( 100, 1 * DEFAULT_COST); // shuffles with no conversion |
| int_def BRANCH_COST ( 200, 2 * DEFAULT_COST); // branch, jmp, call |
| int_def IMUL_COST ( 1000, 10 * DEFAULT_COST); // imul |
| int_def IDIVSI_COST ( 3400, 34 * DEFAULT_COST); // idivdi |
| int_def IDIVDI_COST ( 6600, 66 * DEFAULT_COST); // idivsi |
| int_def FMUL_SINGLE_COST ( 500, 5 * DEFAULT_COST); // fmul, fmadd |
| int_def FMUL_DOUBLE_COST ( 700, 7 * DEFAULT_COST); // fmul, fmadd |
| int_def FDIV_COST ( 2000, 20 * DEFAULT_COST); // fdiv |
| int_def FSQRT_COST ( 2500, 25 * DEFAULT_COST); // fsqrt |
| int_def VOLATILE_REF_COST ( 1000, 10 * DEFAULT_COST); |
| int_def CACHE_MISS_COST ( 2000, 20 * DEFAULT_COST); // typicall cache miss penalty |
| %} |
| |
| |
| |
| //----------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 %{ |
| |
| #include "asm/macroAssembler.hpp" |
| #include "gc/shared/barrierSetAssembler.hpp" |
| #include "gc/shared/cardTable.hpp" |
| #include "gc/shared/cardTableBarrierSet.hpp" |
| #include "gc/shared/collectedHeap.hpp" |
| #include "opto/addnode.hpp" |
| #include "opto/convertnode.hpp" |
| #include "runtime/objectMonitor.hpp" |
| |
| extern RegMask _ANY_REG32_mask; |
| extern RegMask _ANY_REG_mask; |
| extern RegMask _PTR_REG_mask; |
| extern RegMask _NO_SPECIAL_REG32_mask; |
| extern RegMask _NO_SPECIAL_REG_mask; |
| extern RegMask _NO_SPECIAL_PTR_REG_mask; |
| |
| 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 MacroAssembler::far_branch_size(); |
| } |
| |
| static uint size_deopt_handler() { |
| // count auipc + far branch |
| return NativeInstruction::instruction_size + MacroAssembler::far_branch_size(); |
| } |
| }; |
| |
| class Node::PD { |
| public: |
| enum NodeFlags { |
| _last_flag = Node::_last_flag |
| }; |
| }; |
| |
| bool is_CAS(int opcode, bool maybe_volatile); |
| |
| // predicate controlling translation of CompareAndSwapX |
| bool needs_acquiring_load_reserved(const Node *load); |
| |
| // predicate controlling addressing modes |
| bool size_fits_all_mem_uses(AddPNode* addp, int shift); |
| %} |
| |
| source %{ |
| |
| // Derived RegMask with conditionally allocatable registers |
| |
| RegMask _ANY_REG32_mask; |
| RegMask _ANY_REG_mask; |
| RegMask _PTR_REG_mask; |
| RegMask _NO_SPECIAL_REG32_mask; |
| RegMask _NO_SPECIAL_REG_mask; |
| RegMask _NO_SPECIAL_PTR_REG_mask; |
| |
| void reg_mask_init() { |
| |
| _ANY_REG32_mask = _ALL_REG32_mask; |
| _ANY_REG32_mask.Remove(OptoReg::as_OptoReg(x0->as_VMReg())); |
| |
| _ANY_REG_mask = _ALL_REG_mask; |
| _ANY_REG_mask.SUBTRACT(_ZR_REG_mask); |
| |
| _PTR_REG_mask = _ALL_REG_mask; |
| _PTR_REG_mask.SUBTRACT(_ZR_REG_mask); |
| |
| _NO_SPECIAL_REG32_mask = _ALL_REG32_mask; |
| _NO_SPECIAL_REG32_mask.SUBTRACT(_NON_ALLOCATABLE_REG32_mask); |
| |
| _NO_SPECIAL_REG_mask = _ALL_REG_mask; |
| _NO_SPECIAL_REG_mask.SUBTRACT(_NON_ALLOCATABLE_REG_mask); |
| |
| _NO_SPECIAL_PTR_REG_mask = _ALL_REG_mask; |
| _NO_SPECIAL_PTR_REG_mask.SUBTRACT(_NON_ALLOCATABLE_REG_mask); |
| |
| // x27 is not allocatable when compressed oops is on |
| if (UseCompressedOops) { |
| _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(x27->as_VMReg())); |
| _NO_SPECIAL_REG_mask.Remove(OptoReg::as_OptoReg(x27->as_VMReg())); |
| _NO_SPECIAL_PTR_REG_mask.Remove(OptoReg::as_OptoReg(x27->as_VMReg())); |
| } |
| |
| // x8 is not allocatable when PreserveFramePointer is on |
| if (PreserveFramePointer) { |
| _NO_SPECIAL_REG32_mask.Remove(OptoReg::as_OptoReg(x8->as_VMReg())); |
| _NO_SPECIAL_REG_mask.Remove(OptoReg::as_OptoReg(x8->as_VMReg())); |
| _NO_SPECIAL_PTR_REG_mask.Remove(OptoReg::as_OptoReg(x8->as_VMReg())); |
| } |
| } |
| |
| void PhaseOutput::pd_perform_mach_node_analysis() { |
| } |
| |
| int MachNode::pd_alignment_required() const { |
| return 1; |
| } |
| |
| int MachNode::compute_padding(int current_offset) const { |
| return 0; |
| } |
| |
| // is_CAS(int opcode, bool maybe_volatile) |
| // |
| // return true if opcode is one of the possible CompareAndSwapX |
| // values otherwise false. |
| bool is_CAS(int opcode, bool maybe_volatile) |
| { |
| switch (opcode) { |
| // We handle these |
| case Op_CompareAndSwapI: |
| case Op_CompareAndSwapL: |
| case Op_CompareAndSwapP: |
| case Op_CompareAndSwapN: |
| case Op_ShenandoahCompareAndSwapP: |
| case Op_ShenandoahCompareAndSwapN: |
| case Op_CompareAndSwapB: |
| case Op_CompareAndSwapS: |
| case Op_GetAndSetI: |
| case Op_GetAndSetL: |
| case Op_GetAndSetP: |
| case Op_GetAndSetN: |
| case Op_GetAndAddI: |
| case Op_GetAndAddL: |
| return true; |
| case Op_CompareAndExchangeI: |
| case Op_CompareAndExchangeN: |
| case Op_CompareAndExchangeB: |
| case Op_CompareAndExchangeS: |
| case Op_CompareAndExchangeL: |
| case Op_CompareAndExchangeP: |
| case Op_WeakCompareAndSwapB: |
| case Op_WeakCompareAndSwapS: |
| case Op_WeakCompareAndSwapI: |
| case Op_WeakCompareAndSwapL: |
| case Op_WeakCompareAndSwapP: |
| case Op_WeakCompareAndSwapN: |
| case Op_ShenandoahWeakCompareAndSwapP: |
| case Op_ShenandoahWeakCompareAndSwapN: |
| case Op_ShenandoahCompareAndExchangeP: |
| case Op_ShenandoahCompareAndExchangeN: |
| return maybe_volatile; |
| default: |
| return false; |
| } |
| } |
| |
| // predicate controlling translation of CAS |
| // |
| // returns true if CAS needs to use an acquiring load otherwise false |
| bool needs_acquiring_load_reserved(const Node *n) |
| { |
| assert(n != NULL && is_CAS(n->Opcode(), true), "expecting a compare and swap"); |
| |
| LoadStoreNode* ldst = n->as_LoadStore(); |
| if (n != NULL && is_CAS(n->Opcode(), false)) { |
| assert(ldst != NULL && ldst->trailing_membar() != NULL, "expected trailing membar"); |
| } else { |
| return ldst != NULL && ldst->trailing_membar() != NULL; |
| } |
| // so we can just return true here |
| return true; |
| } |
| #define __ _masm. |
| |
| // advance declarations for helper functions to convert register |
| // indices to register objects |
| |
| // the ad file has to provide implementations of certain methods |
| // expected by the generic code |
| // |
| // REQUIRED FUNCTIONALITY |
| |
| //============================================================================= |
| |
| // !!!!! Special hack to get all types of calls to specify the byte offset |
| // from the start of the call to the point where the return address |
| // will point. |
| |
| int MachCallStaticJavaNode::ret_addr_offset() |
| { |
| // jal |
| return 1 * NativeInstruction::instruction_size; |
| } |
| |
| int MachCallDynamicJavaNode::ret_addr_offset() |
| { |
| return 7 * NativeInstruction::instruction_size; // movptr, jal |
| } |
| |
| int MachCallRuntimeNode::ret_addr_offset() { |
| // for generated stubs the call will be |
| // jal(addr) |
| // or with far branches |
| // jal(trampoline_stub) |
| // for real runtime callouts it will be 11 instructions |
| // see riscv_enc_java_to_runtime |
| // la(t1, retaddr) -> auipc + addi |
| // la(t0, RuntimeAddress(addr)) -> lui + addi + slli + addi + slli + addi |
| // addi(sp, sp, -2 * wordSize) -> addi |
| // sd(t1, Address(sp, wordSize)) -> sd |
| // jalr(t0) -> jalr |
| CodeBlob *cb = CodeCache::find_blob(_entry_point); |
| if (cb != NULL) { |
| return 1 * NativeInstruction::instruction_size; |
| } else { |
| return 11 * NativeInstruction::instruction_size; |
| } |
| } |
| |
| // |
| // Compute padding required for nodes which need alignment |
| // |
| |
| // With RVC a call instruction may get 2-byte aligned. |
| // The address of the call instruction needs to be 4-byte aligned to |
| // ensure that it does not span a cache line so that it can be patched. |
| int CallStaticJavaDirectNode::compute_padding(int current_offset) const |
| { |
| // to make sure the address of jal 4-byte aligned. |
| return align_up(current_offset, alignment_required()) - current_offset; |
| } |
| |
| // With RVC a call instruction may get 2-byte aligned. |
| // The address of the call instruction needs to be 4-byte aligned to |
| // ensure that it does not span a cache line so that it can be patched. |
| int CallDynamicJavaDirectNode::compute_padding(int current_offset) const |
| { |
| // skip the movptr in MacroAssembler::ic_call(): |
| // lui + addi + slli + addi + slli + addi |
| // Though movptr() has already 4-byte aligned with or without RVC, |
| // We need to prevent from further changes by explicitly calculating the size. |
| const int movptr_size = 6 * NativeInstruction::instruction_size; |
| current_offset += movptr_size; |
| // to make sure the address of jal 4-byte aligned. |
| return align_up(current_offset, alignment_required()) - current_offset; |
| } |
| |
| //============================================================================= |
| |
| #ifndef PRODUCT |
| void MachBreakpointNode::format(PhaseRegAlloc *ra_, outputStream *st) const { |
| assert_cond(st != NULL); |
| st->print("BREAKPOINT"); |
| } |
| #endif |
| |
| void MachBreakpointNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { |
| C2_MacroAssembler _masm(&cbuf); |
| __ ebreak(); |
| } |
| |
| uint MachBreakpointNode::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", _count); |
| } |
| #endif |
| |
| void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc*) const { |
| C2_MacroAssembler _masm(&cbuf); |
| Assembler::CompressibleRegion cr(&_masm); // nops shall be 2-byte under RVC for alignment purposes. |
| for (int i = 0; i < _count; i++) { |
| __ nop(); |
| } |
| } |
| |
| uint MachNopNode::size(PhaseRegAlloc*) const { |
| return _count * (UseRVC ? NativeInstruction::compressed_instruction_size : NativeInstruction::instruction_size); |
| } |
| |
| //============================================================================= |
| const RegMask& MachConstantBaseNode::_out_RegMask = RegMask::Empty; |
| |
| int ConstantTable::calculate_table_base_offset() const { |
| return 0; // absolute addressing, no 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 { |
| // Empty encoding |
| } |
| |
| uint MachConstantBaseNode::size(PhaseRegAlloc* ra_) const { |
| return 0; |
| } |
| |
| #ifndef PRODUCT |
| void MachConstantBaseNode::format(PhaseRegAlloc* ra_, outputStream* st) const { |
| assert_cond(st != NULL); |
| st->print("-- \t// MachConstantBaseNode (empty encoding)"); |
| } |
| #endif |
| |
| #ifndef PRODUCT |
| void MachPrologNode::format(PhaseRegAlloc *ra_, outputStream *st) const { |
| assert_cond(st != NULL && ra_ != NULL); |
| Compile* C = ra_->C; |
| |
| int framesize = C->output()->frame_slots() << LogBytesPerInt; |
| |
| if (C->output()->need_stack_bang(framesize)) { |
| st->print("# stack bang size=%d\n\t", framesize); |
| } |
| |
| st->print("sd fp, [sp, #%d]\n\t", - 2 * wordSize); |
| st->print("sd ra, [sp, #%d]\n\t", - wordSize); |
| if (PreserveFramePointer) { st->print("sub fp, sp, #%d\n\t", 2 * wordSize); } |
| st->print("sub sp, sp, #%d\n\t", framesize); |
| |
| if (C->stub_function() == NULL && BarrierSet::barrier_set()->barrier_set_nmethod() != NULL) { |
| st->print("ld t0, [guard]\n\t"); |
| st->print("membar LoadLoad\n\t"); |
| st->print("ld t1, [xthread, #thread_disarmed_guard_value_offset]\n\t"); |
| st->print("beq t0, t1, skip\n\t"); |
| st->print("jalr #nmethod_entry_barrier_stub\n\t"); |
| st->print("j skip\n\t"); |
| st->print("guard: int\n\t"); |
| st->print("skip:\n\t"); |
| } |
| } |
| #endif |
| |
| void MachPrologNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { |
| assert_cond(ra_ != NULL); |
| Compile* C = ra_->C; |
| C2_MacroAssembler _masm(&cbuf); |
| |
| // n.b. frame size includes space for return pc and fp |
| const int framesize = C->output()->frame_size_in_bytes(); |
| |
| // insert a nop at the start of the prolog so we can patch in a |
| // branch if we need to invalidate the method later |
| { |
| Assembler::IncompressibleRegion ir(&_masm); // keep the nop as 4 bytes for patching. |
| MacroAssembler::assert_alignment(__ pc()); |
| __ nop(); // 4 bytes |
| } |
| |
| assert_cond(C != NULL); |
| |
| if (C->clinit_barrier_on_entry()) { |
| assert(!C->method()->holder()->is_not_initialized(), "initialization should have been started"); |
| |
| Label L_skip_barrier; |
| |
| __ mov_metadata(t1, C->method()->holder()->constant_encoding()); |
| __ clinit_barrier(t1, t0, &L_skip_barrier); |
| __ far_jump(RuntimeAddress(SharedRuntime::get_handle_wrong_method_stub())); |
| __ bind(L_skip_barrier); |
| } |
| |
| int bangsize = C->output()->bang_size_in_bytes(); |
| if (C->output()->need_stack_bang(bangsize)) { |
| __ generate_stack_overflow_check(bangsize); |
| } |
| |
| __ build_frame(framesize); |
| |
| if (C->stub_function() == NULL) { |
| BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler(); |
| if (BarrierSet::barrier_set()->barrier_set_nmethod() != NULL) { |
| // Dummy labels for just measuring the code size |
| Label dummy_slow_path; |
| Label dummy_continuation; |
| Label dummy_guard; |
| Label* slow_path = &dummy_slow_path; |
| Label* continuation = &dummy_continuation; |
| Label* guard = &dummy_guard; |
| if (!Compile::current()->output()->in_scratch_emit_size()) { |
| // Use real labels from actual stub when not emitting code for purpose of measuring its size |
| C2EntryBarrierStub* stub = new (Compile::current()->comp_arena()) C2EntryBarrierStub(); |
| Compile::current()->output()->add_stub(stub); |
| slow_path = &stub->entry(); |
| continuation = &stub->continuation(); |
| guard = &stub->guard(); |
| } |
| // In the C2 code, we move the non-hot part of nmethod entry barriers out-of-line to a stub. |
| bs->nmethod_entry_barrier(&_masm, slow_path, continuation, guard); |
| } |
| } |
| |
| if (VerifyStackAtCalls) { |
| Unimplemented(); |
| } |
| |
| C->output()->set_frame_complete(cbuf.insts_size()); |
| |
| if (C->has_mach_constant_base_node()) { |
| // NOTE: We set the table base offset here because users might be |
| // emitted before MachConstantBaseNode. |
| ConstantTable& constant_table = C->output()->constant_table(); |
| constant_table.set_table_base_offset(constant_table.calculate_table_base_offset()); |
| } |
| } |
| |
| uint MachPrologNode::size(PhaseRegAlloc* ra_) const |
| { |
| assert_cond(ra_ != NULL); |
| return MachNode::size(ra_); // too many variables; just compute it |
| // the hard way |
| } |
| |
| int MachPrologNode::reloc() const |
| { |
| return 0; |
| } |
| |
| //============================================================================= |
| |
| #ifndef PRODUCT |
| void MachEpilogNode::format(PhaseRegAlloc *ra_, outputStream *st) const { |
| assert_cond(st != NULL && ra_ != NULL); |
| Compile* C = ra_->C; |
| assert_cond(C != NULL); |
| int framesize = C->output()->frame_size_in_bytes(); |
| |
| st->print("# pop frame %d\n\t", framesize); |
| |
| if (framesize == 0) { |
| st->print("ld ra, [sp,#%d]\n\t", (2 * wordSize)); |
| st->print("ld fp, [sp,#%d]\n\t", (3 * wordSize)); |
| st->print("add sp, sp, #%d\n\t", (2 * wordSize)); |
| } else { |
| st->print("add sp, sp, #%d\n\t", framesize); |
| st->print("ld ra, [sp,#%d]\n\t", - 2 * wordSize); |
| st->print("ld fp, [sp,#%d]\n\t", - wordSize); |
| } |
| |
| if (do_polling() && C->is_method_compilation()) { |
| st->print("# test polling word\n\t"); |
| st->print("ld t0, [xthread,#%d]\n\t", in_bytes(JavaThread::polling_word_offset())); |
| st->print("bgtu sp, t0, #slow_path"); |
| } |
| } |
| #endif |
| |
| void MachEpilogNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { |
| assert_cond(ra_ != NULL); |
| Compile* C = ra_->C; |
| C2_MacroAssembler _masm(&cbuf); |
| assert_cond(C != NULL); |
| int framesize = C->output()->frame_size_in_bytes(); |
| |
| __ remove_frame(framesize); |
| |
| if (StackReservedPages > 0 && C->has_reserved_stack_access()) { |
| __ reserved_stack_check(); |
| } |
| |
| if (do_polling() && C->is_method_compilation()) { |
| Label dummy_label; |
| Label* code_stub = &dummy_label; |
| if (!C->output()->in_scratch_emit_size()) { |
| C2SafepointPollStub* stub = new (C->comp_arena()) C2SafepointPollStub(__ offset()); |
| C->output()->add_stub(stub); |
| code_stub = &stub->entry(); |
| } |
| __ relocate(relocInfo::poll_return_type); |
| __ safepoint_poll(*code_stub, true /* at_return */, false /* acquire */, true /* in_nmethod */); |
| } |
| } |
| |
| uint MachEpilogNode::size(PhaseRegAlloc *ra_) const { |
| assert_cond(ra_ != NULL); |
| // Variable size. Determine dynamically. |
| return MachNode::size(ra_); |
| } |
| |
| int MachEpilogNode::reloc() const { |
| // Return number of relocatable values contained in this instruction. |
| return 1; // 1 for polling page. |
| } |
| const Pipeline * MachEpilogNode::pipeline() const { |
| return MachNode::pipeline_class(); |
| } |
| |
| //============================================================================= |
| |
| // Figure out which register class each belongs in: rc_int, rc_float or |
| // rc_stack. |
| enum RC { rc_bad, rc_int, rc_float, rc_vector, rc_stack }; |
| |
| static enum RC rc_class(OptoReg::Name reg) { |
| |
| if (reg == OptoReg::Bad) { |
| return rc_bad; |
| } |
| |
| // we have 30 int registers * 2 halves |
| // (t0 and t1 are omitted) |
| int slots_of_int_registers = Register::max_slots_per_register * (Register::number_of_registers - 2); |
| if (reg < slots_of_int_registers) { |
| return rc_int; |
| } |
| |
| // we have 32 float register * 2 halves |
| int slots_of_float_registers = FloatRegister::max_slots_per_register * FloatRegister::number_of_registers; |
| if (reg < slots_of_int_registers + slots_of_float_registers) { |
| return rc_float; |
| } |
| |
| // we have 32 vector register * 4 halves |
| int slots_of_vector_registers = VectorRegister::max_slots_per_register * VectorRegister::number_of_registers; |
| if (reg < slots_of_int_registers + slots_of_float_registers + slots_of_vector_registers) { |
| return rc_vector; |
| } |
| |
| // Between vector regs & stack is the flags regs. |
| assert(OptoReg::is_stack(reg), "blow up if spilling flags"); |
| |
| return rc_stack; |
| } |
| |
| uint MachSpillCopyNode::implementation(CodeBuffer *cbuf, PhaseRegAlloc *ra_, bool do_size, outputStream *st) const { |
| assert_cond(ra_ != NULL); |
| Compile* C = ra_->C; |
| |
| // Get registers to move. |
| OptoReg::Name src_hi = ra_->get_reg_second(in(1)); |
| OptoReg::Name src_lo = ra_->get_reg_first(in(1)); |
| OptoReg::Name dst_hi = ra_->get_reg_second(this); |
| OptoReg::Name dst_lo = ra_->get_reg_first(this); |
| |
| enum RC src_hi_rc = rc_class(src_hi); |
| enum RC src_lo_rc = rc_class(src_lo); |
| enum RC dst_hi_rc = rc_class(dst_hi); |
| enum RC dst_lo_rc = rc_class(dst_lo); |
| |
| assert(src_lo != OptoReg::Bad && dst_lo != OptoReg::Bad, "must move at least 1 register"); |
| |
| if (src_hi != OptoReg::Bad && !bottom_type()->isa_vectmask()) { |
| assert((src_lo & 1) == 0 && src_lo + 1 == src_hi && |
| (dst_lo & 1) == 0 && dst_lo + 1 == dst_hi, |
| "expected aligned-adjacent pairs"); |
| } |
| |
| if (src_lo == dst_lo && src_hi == dst_hi) { |
| return 0; // Self copy, no move. |
| } |
| |
| bool is64 = (src_lo & 1) == 0 && src_lo + 1 == src_hi && |
| (dst_lo & 1) == 0 && dst_lo + 1 == dst_hi; |
| int src_offset = ra_->reg2offset(src_lo); |
| int dst_offset = ra_->reg2offset(dst_lo); |
| |
| if (bottom_type()->isa_vect() != NULL) { |
| uint ireg = ideal_reg(); |
| if (ireg == Op_VecA && cbuf) { |
| C2_MacroAssembler _masm(cbuf); |
| int vector_reg_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE); |
| if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) { |
| // stack to stack |
| __ spill_copy_vector_stack_to_stack(src_offset, dst_offset, |
| vector_reg_size_in_bytes); |
| } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_stack) { |
| // vpr to stack |
| __ spill(as_VectorRegister(Matcher::_regEncode[src_lo]), ra_->reg2offset(dst_lo)); |
| } else if (src_lo_rc == rc_stack && dst_lo_rc == rc_vector) { |
| // stack to vpr |
| __ unspill(as_VectorRegister(Matcher::_regEncode[dst_lo]), ra_->reg2offset(src_lo)); |
| } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_vector) { |
| // vpr to vpr |
| __ vmv1r_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); |
| } else { |
| ShouldNotReachHere(); |
| } |
| } else if (bottom_type()->isa_vectmask() && cbuf) { |
| C2_MacroAssembler _masm(cbuf); |
| int vmask_size_in_bytes = Matcher::scalable_predicate_reg_slots() * 32 / 8; |
| if (src_lo_rc == rc_stack && dst_lo_rc == rc_stack) { |
| // stack to stack |
| __ spill_copy_vmask_stack_to_stack(src_offset, dst_offset, |
| vmask_size_in_bytes); |
| } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_stack) { |
| // vmask to stack |
| __ spill_vmask(as_VectorRegister(Matcher::_regEncode[src_lo]), ra_->reg2offset(dst_lo)); |
| } else if (src_lo_rc == rc_stack && dst_lo_rc == rc_vector) { |
| // stack to vmask |
| __ unspill_vmask(as_VectorRegister(Matcher::_regEncode[dst_lo]), ra_->reg2offset(src_lo)); |
| } else if (src_lo_rc == rc_vector && dst_lo_rc == rc_vector) { |
| // vmask to vmask |
| __ vmv1r_v(as_VectorRegister(Matcher::_regEncode[dst_lo]), as_VectorRegister(Matcher::_regEncode[src_lo])); |
| } else { |
| ShouldNotReachHere(); |
| } |
| } |
| } else if (cbuf != NULL) { |
| C2_MacroAssembler _masm(cbuf); |
| switch (src_lo_rc) { |
| case rc_int: |
| if (dst_lo_rc == rc_int) { // gpr --> gpr copy |
| if (!is64 && this->ideal_reg() != Op_RegI) { // zero extended for narrow oop or klass |
| __ zero_extend(as_Register(Matcher::_regEncode[dst_lo]), as_Register(Matcher::_regEncode[src_lo]), 32); |
| } else { |
| __ mv(as_Register(Matcher::_regEncode[dst_lo]), as_Register(Matcher::_regEncode[src_lo])); |
| } |
| } else if (dst_lo_rc == rc_float) { // gpr --> fpr copy |
| if (is64) { |
| __ fmv_d_x(as_FloatRegister(Matcher::_regEncode[dst_lo]), |
| as_Register(Matcher::_regEncode[src_lo])); |
| } else { |
| __ fmv_w_x(as_FloatRegister(Matcher::_regEncode[dst_lo]), |
| as_Register(Matcher::_regEncode[src_lo])); |
| } |
| } else { // gpr --> stack spill |
| assert(dst_lo_rc == rc_stack, "spill to bad register class"); |
| __ spill(as_Register(Matcher::_regEncode[src_lo]), is64, dst_offset); |
| } |
| break; |
| case rc_float: |
| if (dst_lo_rc == rc_int) { // fpr --> gpr copy |
| if (is64) { |
| __ fmv_x_d(as_Register(Matcher::_regEncode[dst_lo]), |
| as_FloatRegister(Matcher::_regEncode[src_lo])); |
| } else { |
| __ fmv_x_w(as_Register(Matcher::_regEncode[dst_lo]), |
| as_FloatRegister(Matcher::_regEncode[src_lo])); |
| } |
| } else if (dst_lo_rc == rc_float) { // fpr --> fpr copy |
| if (is64) { |
| __ fmv_d(as_FloatRegister(Matcher::_regEncode[dst_lo]), |
| as_FloatRegister(Matcher::_regEncode[src_lo])); |
| } else { |
| __ fmv_s(as_FloatRegister(Matcher::_regEncode[dst_lo]), |
| as_FloatRegister(Matcher::_regEncode[src_lo])); |
| } |
| } else { // fpr --> stack spill |
| assert(dst_lo_rc == rc_stack, "spill to bad register class"); |
| __ spill(as_FloatRegister(Matcher::_regEncode[src_lo]), |
| is64, dst_offset); |
| } |
| break; |
| case rc_stack: |
| if (dst_lo_rc == rc_int) { // stack --> gpr load |
| if (this->ideal_reg() == Op_RegI) { |
| __ unspill(as_Register(Matcher::_regEncode[dst_lo]), is64, src_offset); |
| } else { // // zero extended for narrow oop or klass |
| __ unspillu(as_Register(Matcher::_regEncode[dst_lo]), is64, src_offset); |
| } |
| } else if (dst_lo_rc == rc_float) { // stack --> fpr load |
| __ unspill(as_FloatRegister(Matcher::_regEncode[dst_lo]), |
| is64, src_offset); |
| } else { // stack --> stack copy |
| assert(dst_lo_rc == rc_stack, "spill to bad register class"); |
| if (this->ideal_reg() == Op_RegI) { |
| __ unspill(t0, is64, src_offset); |
| } else { // zero extended for narrow oop or klass |
| __ unspillu(t0, is64, src_offset); |
| } |
| __ spill(t0, is64, dst_offset); |
| } |
| break; |
| default: |
| ShouldNotReachHere(); |
| } |
| } |
| |
| if (st != NULL) { |
| st->print("spill "); |
| if (src_lo_rc == rc_stack) { |
| st->print("[sp, #%d] -> ", src_offset); |
| } else { |
| st->print("%s -> ", Matcher::regName[src_lo]); |
| } |
| if (dst_lo_rc == rc_stack) { |
| st->print("[sp, #%d]", dst_offset); |
| } else { |
| st->print("%s", Matcher::regName[dst_lo]); |
| } |
| if (bottom_type()->isa_vect() && !bottom_type()->isa_vectmask()) { |
| int vsize = 0; |
| if (ideal_reg() == Op_VecA) { |
| vsize = Matcher::scalable_vector_reg_size(T_BYTE) * 8; |
| } else { |
| ShouldNotReachHere(); |
| } |
| st->print("\t# vector spill size = %d", vsize); |
| } else if (ideal_reg() == Op_RegVectMask) { |
| assert(Matcher::supports_scalable_vector(), "bad register type for spill"); |
| int vsize = Matcher::scalable_predicate_reg_slots() * 32; |
| st->print("\t# vmask spill size = %d", vsize); |
| } else { |
| st->print("\t# spill size = %d", is64 ? 64 : 32); |
| } |
| } |
| |
| return 0; |
| } |
| |
| #ifndef PRODUCT |
| void MachSpillCopyNode::format(PhaseRegAlloc *ra_, outputStream *st) const { |
| if (ra_ == NULL) { |
| st->print("N%d = SpillCopy(N%d)", _idx, in(1)->_idx); |
| } else { |
| 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 BoxLockNode::format(PhaseRegAlloc *ra_, outputStream *st) const { |
| assert_cond(ra_ != NULL && st != NULL); |
| int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); |
| int reg = ra_->get_reg_first(this); |
| st->print("add %s, sp, #%d\t# box lock", |
| Matcher::regName[reg], offset); |
| } |
| #endif |
| |
| void BoxLockNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *ra_) const { |
| C2_MacroAssembler _masm(&cbuf); |
| Assembler::IncompressibleRegion ir(&_masm); // Fixed length: see BoxLockNode::size() |
| |
| assert_cond(ra_ != NULL); |
| int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); |
| int reg = ra_->get_encode(this); |
| |
| if (Assembler::is_simm12(offset)) { |
| __ addi(as_Register(reg), sp, offset); |
| } else { |
| __ li32(t0, offset); |
| __ add(as_Register(reg), sp, t0); |
| } |
| } |
| |
| uint BoxLockNode::size(PhaseRegAlloc *ra_) const { |
| // BoxLockNode is not a MachNode, so we can't just call MachNode::size(ra_). |
| int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); |
| |
| if (Assembler::is_simm12(offset)) { |
| return NativeInstruction::instruction_size; |
| } else { |
| return 3 * NativeInstruction::instruction_size; // lui + addiw + add; |
| } |
| } |
| |
| //============================================================================= |
| |
| #ifndef PRODUCT |
| void MachUEPNode::format(PhaseRegAlloc* ra_, outputStream* st) const |
| { |
| assert_cond(st != NULL); |
| st->print_cr("# MachUEPNode"); |
| if (UseCompressedClassPointers) { |
| st->print_cr("\tlwu t0, [j_rarg0, oopDesc::klass_offset_in_bytes()]\t# compressed klass"); |
| if (CompressedKlassPointers::shift() != 0) { |
| st->print_cr("\tdecode_klass_not_null t0, t0"); |
| } |
| } else { |
| st->print_cr("\tld t0, [j_rarg0, oopDesc::klass_offset_in_bytes()]\t# compressed klass"); |
| } |
| st->print_cr("\tbeq t0, t1, ic_hit"); |
| st->print_cr("\tj, SharedRuntime::_ic_miss_stub\t # Inline cache check"); |
| st->print_cr("\tic_hit:"); |
| } |
| #endif |
| |
| void MachUEPNode::emit(CodeBuffer& cbuf, PhaseRegAlloc* ra_) const |
| { |
| // This is the unverified entry point. |
| C2_MacroAssembler _masm(&cbuf); |
| |
| Label skip; |
| __ cmp_klass(j_rarg0, t1, t0, t2 /* call-clobbered t2 as a tmp */, skip); |
| __ far_jump(RuntimeAddress(SharedRuntime::get_ic_miss_stub())); |
| __ bind(skip); |
| |
| // These NOPs are critical so that verified entry point is properly |
| // 4 bytes aligned for patching by NativeJump::patch_verified_entry() |
| __ align(NativeInstruction::instruction_size); |
| } |
| |
| uint MachUEPNode::size(PhaseRegAlloc* ra_) const |
| { |
| assert_cond(ra_ != NULL); |
| return MachNode::size(ra_); |
| } |
| |
| // REQUIRED EMIT CODE |
| |
| //============================================================================= |
| |
| // Emit exception handler code. |
| int HandlerImpl::emit_exception_handler(CodeBuffer& cbuf) |
| { |
| // la_patchable t0, #exception_blob_entry_point |
| // jr (offset)t0 |
| // or |
| // j #exception_blob_entry_point |
| // Note that the code buffer's insts_mark is always relative to insts. |
| // That's why we must use the macroassembler to generate a handler. |
| C2_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(); |
| __ far_jump(RuntimeAddress(OptoRuntime::exception_blob()->entry_point())); |
| assert(__ offset() - offset <= (int) size_exception_handler(), "overflow"); |
| __ end_a_stub(); |
| return offset; |
| } |
| |
| // Emit deopt handler code. |
| int HandlerImpl::emit_deopt_handler(CodeBuffer& cbuf) |
| { |
| // Note that the code buffer's insts_mark is always relative to insts. |
| // That's why we must use the macroassembler to generate a handler. |
| C2_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(); |
| |
| __ auipc(ra, 0); |
| __ far_jump(RuntimeAddress(SharedRuntime::deopt_blob()->unpack())); |
| |
| assert(__ offset() - offset <= (int) size_deopt_handler(), "overflow"); |
| __ end_a_stub(); |
| return offset; |
| |
| } |
| // REQUIRED MATCHER CODE |
| |
| //============================================================================= |
| |
| const bool Matcher::match_rule_supported(int opcode) { |
| if (!has_match_rule(opcode)) { |
| return false; |
| } |
| |
| switch (opcode) { |
| case Op_OnSpinWait: |
| return VM_Version::supports_on_spin_wait(); |
| case Op_CacheWB: // fall through |
| case Op_CacheWBPreSync: // fall through |
| case Op_CacheWBPostSync: |
| if (!VM_Version::supports_data_cache_line_flush()) { |
| return false; |
| } |
| break; |
| |
| case Op_StrCompressedCopy: // fall through |
| case Op_StrInflatedCopy: // fall through |
| case Op_CountPositives: |
| return UseRVV; |
| |
| case Op_EncodeISOArray: |
| return UseRVV && SpecialEncodeISOArray; |
| |
| case Op_PopCountI: |
| case Op_PopCountL: |
| return UsePopCountInstruction; |
| |
| case Op_RotateRight: |
| case Op_RotateLeft: |
| case Op_CountLeadingZerosI: |
| case Op_CountLeadingZerosL: |
| case Op_CountTrailingZerosI: |
| case Op_CountTrailingZerosL: |
| return UseZbb; |
| } |
| |
| return true; // Per default match rules are supported. |
| } |
| |
| const RegMask* Matcher::predicate_reg_mask(void) { |
| return &_VMASK_REG_mask; |
| } |
| |
| const TypeVectMask* Matcher::predicate_reg_type(const Type* elemTy, int length) { |
| return new TypeVectMask(elemTy, length); |
| } |
| |
| // Vector calling convention not yet implemented. |
| const bool Matcher::supports_vector_calling_convention(void) { |
| return false; |
| } |
| |
| OptoRegPair Matcher::vector_return_value(uint ideal_reg) { |
| Unimplemented(); |
| return OptoRegPair(0, 0); |
| } |
| |
| // 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. |
| // |---label(L1)-----| |
| // |-----------------| |
| // |-----------------|----------eq: float------------------- |
| // |-----------------| // far_cmpD_branch | cmpD_branch |
| // |------- ---------| feq; | feq; |
| // |-far_cmpD_branch-| beqz done; | bnez L; |
| // |-----------------| j L; | |
| // |-----------------| bind(done); | |
| // |-----------------|-------------------------------------- |
| // |-----------------| // so shortBrSize = br_size - 4; |
| // |-----------------| // so offs = offset - shortBrSize + 4; |
| // |---label(L2)-----| |
| bool Matcher::is_short_branch_offset(int rule, int br_size, int offset) { |
| // The passed offset is relative to address of the branch. |
| int shortBrSize = br_size - 4; |
| int offs = offset - shortBrSize + 4; |
| return (-4096 <= offs && offs < 4096); |
| } |
| |
| // Vector width in bytes. |
| const int Matcher::vector_width_in_bytes(BasicType bt) { |
| if (UseRVV) { |
| // The MaxVectorSize should have been set by detecting RVV max vector register size when check UseRVV. |
| // MaxVectorSize == VM_Version::_initial_vector_length |
| int size = MaxVectorSize; |
| // Minimum 2 values in vector |
| if (size < 2 * type2aelembytes(bt)) size = 0; |
| // But never < 4 |
| if (size < 4) size = 0; |
| return size; |
| } |
| return 0; |
| } |
| |
| // Limits on vector size (number of elements) loaded into vector. |
| const int Matcher::max_vector_size(const BasicType bt) { |
| return vector_width_in_bytes(bt) / type2aelembytes(bt); |
| } |
| |
| const int Matcher::min_vector_size(const BasicType bt) { |
| int max_size = max_vector_size(bt); |
| // Limit the min vector size to 8 bytes. |
| int size = 8 / type2aelembytes(bt); |
| if (bt == T_BYTE) { |
| // To support vector api shuffle/rearrange. |
| size = 4; |
| } else if (bt == T_BOOLEAN) { |
| // To support vector api load/store mask. |
| size = 2; |
| } |
| if (size < 2) size = 2; |
| return MIN2(size, max_size); |
| } |
| |
| const int Matcher::superword_max_vector_size(const BasicType bt) { |
| return Matcher::max_vector_size(bt); |
| } |
| |
| // Vector ideal reg. |
| const uint Matcher::vector_ideal_reg(int len) { |
| assert(MaxVectorSize >= len, ""); |
| if (UseRVV) { |
| return Op_VecA; |
| } |
| |
| ShouldNotReachHere(); |
| return 0; |
| } |
| |
| const int Matcher::scalable_vector_reg_size(const BasicType bt) { |
| return Matcher::max_vector_size(bt); |
| } |
| |
| MachOper* Matcher::pd_specialize_generic_vector_operand(MachOper* original_opnd, uint ideal_reg, bool is_temp) { |
| ShouldNotReachHere(); // generic vector operands not supported |
| return NULL; |
| } |
| |
| bool Matcher::is_reg2reg_move(MachNode* m) { |
| ShouldNotReachHere(); // generic vector operands not supported |
| return false; |
| } |
| |
| bool Matcher::is_generic_vector(MachOper* opnd) { |
| ShouldNotReachHere(); // generic vector operands not supported |
| return false; |
| } |
| |
| // 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) |
| { |
| return |
| reg == R10_num || reg == R10_H_num || |
| reg == R11_num || reg == R11_H_num || |
| reg == R12_num || reg == R12_H_num || |
| reg == R13_num || reg == R13_H_num || |
| reg == R14_num || reg == R14_H_num || |
| reg == R15_num || reg == R15_H_num || |
| reg == R16_num || reg == R16_H_num || |
| reg == R17_num || reg == R17_H_num || |
| reg == F10_num || reg == F10_H_num || |
| reg == F11_num || reg == F11_H_num || |
| reg == F12_num || reg == F12_H_num || |
| reg == F13_num || reg == F13_H_num || |
| reg == F14_num || reg == F14_H_num || |
| reg == F15_num || reg == F15_H_num || |
| reg == F16_num || reg == F16_H_num || |
| reg == F17_num || reg == F17_H_num; |
| } |
| |
| bool Matcher::is_spillable_arg(int reg) |
| { |
| return can_be_java_arg(reg); |
| } |
| |
| uint Matcher::int_pressure_limit() |
| { |
| // A derived pointer is live at CallNode and then is flagged by RA |
| // as a spilled LRG. Spilling heuristics(Spill-USE) explicitly skip |
| // derived pointers and lastly fail to spill after reaching maximum |
| // number of iterations. Lowering the default pressure threshold to |
| // (_NO_SPECIAL_REG32_mask.Size() minus 1) forces CallNode to become |
| // a high register pressure area of the code so that split_DEF can |
| // generate DefinitionSpillCopy for the derived pointer. |
| uint default_int_pressure_threshold = _NO_SPECIAL_REG32_mask.Size() - 1; |
| if (!PreserveFramePointer) { |
| // When PreserveFramePointer is off, frame pointer is allocatable, |
| // but different from other SOC registers, it is excluded from |
| // fatproj's mask because its save type is No-Save. Decrease 1 to |
| // ensure high pressure at fatproj when PreserveFramePointer is off. |
| // See check_pressure_at_fatproj(). |
| default_int_pressure_threshold--; |
| } |
| return (INTPRESSURE == -1) ? default_int_pressure_threshold : INTPRESSURE; |
| } |
| |
| uint Matcher::float_pressure_limit() |
| { |
| // _FLOAT_REG_mask is generated by adlc from the float_reg register class. |
| return (FLOATPRESSURE == -1) ? _FLOAT_REG_mask.Size() : FLOATPRESSURE; |
| } |
| |
| bool Matcher::use_asm_for_ldiv_by_con(jlong divisor) { |
| return false; |
| } |
| |
| 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 FP_REG_mask(); |
| } |
| |
| bool size_fits_all_mem_uses(AddPNode* addp, int shift) { |
| assert_cond(addp != NULL); |
| for (DUIterator_Fast imax, i = addp->fast_outs(imax); i < imax; i++) { |
| Node* u = addp->fast_out(i); |
| if (u != NULL && u->is_Mem()) { |
| int opsize = u->as_Mem()->memory_size(); |
| assert(opsize > 0, "unexpected memory operand size"); |
| if (u->as_Mem()->memory_size() != (1 << shift)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| // Should the Matcher clone input 'm' of node 'n'? |
| bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) { |
| assert_cond(m != NULL); |
| if (is_vshift_con_pattern(n, m)) { // ShiftV src (ShiftCntV con) |
| mstack.push(m, Visit); // m = ShiftCntV |
| return true; |
| } |
| return false; |
| } |
| |
| // Should the Matcher clone shifts on addressing modes, expecting them |
| // to be subsumed into complex addressing expressions or compute them |
| // into registers? |
| bool Matcher::pd_clone_address_expressions(AddPNode* m, Matcher::MStack& mstack, VectorSet& address_visited) { |
| return clone_base_plus_offset_address(m, mstack, address_visited); |
| } |
| |
| %} |
| |
| |
| |
| //----------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 %{ |
| // BEGIN Non-volatile memory access |
| |
| enc_class riscv_enc_mov_imm(iRegIorL dst, immIorL src) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| int64_t con = (int64_t)$src$$constant; |
| Register dst_reg = as_Register($dst$$reg); |
| __ mv(dst_reg, con); |
| %} |
| |
| enc_class riscv_enc_mov_p(iRegP dst, immP src) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register dst_reg = as_Register($dst$$reg); |
| address con = (address)$src$$constant; |
| if (con == NULL || con == (address)1) { |
| ShouldNotReachHere(); |
| } else { |
| relocInfo::relocType rtype = $src->constant_reloc(); |
| if (rtype == relocInfo::oop_type) { |
| __ movoop(dst_reg, (jobject)con); |
| } else if (rtype == relocInfo::metadata_type) { |
| __ mov_metadata(dst_reg, (Metadata*)con); |
| } else { |
| assert(rtype == relocInfo::none, "unexpected reloc type"); |
| __ mv(dst_reg, $src$$constant); |
| } |
| } |
| %} |
| |
| enc_class riscv_enc_mov_p1(iRegP dst) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register dst_reg = as_Register($dst$$reg); |
| __ mv(dst_reg, 1); |
| %} |
| |
| enc_class riscv_enc_mov_byte_map_base(iRegP dst) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| __ load_byte_map_base($dst$$Register); |
| %} |
| |
| enc_class riscv_enc_mov_n(iRegN dst, immN src) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register dst_reg = as_Register($dst$$reg); |
| address con = (address)$src$$constant; |
| if (con == NULL) { |
| ShouldNotReachHere(); |
| } else { |
| relocInfo::relocType rtype = $src->constant_reloc(); |
| assert(rtype == relocInfo::oop_type, "unexpected reloc type"); |
| __ set_narrow_oop(dst_reg, (jobject)con); |
| } |
| %} |
| |
| enc_class riscv_enc_mov_zero(iRegNorP dst) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register dst_reg = as_Register($dst$$reg); |
| __ mv(dst_reg, zr); |
| %} |
| |
| enc_class riscv_enc_mov_nk(iRegN dst, immNKlass src) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register dst_reg = as_Register($dst$$reg); |
| address con = (address)$src$$constant; |
| if (con == NULL) { |
| ShouldNotReachHere(); |
| } else { |
| relocInfo::relocType rtype = $src->constant_reloc(); |
| assert(rtype == relocInfo::metadata_type, "unexpected reloc type"); |
| __ set_narrow_klass(dst_reg, (Klass *)con); |
| } |
| %} |
| |
| enc_class riscv_enc_cmpxchgw(iRegINoSp res, memory mem, iRegI oldval, iRegI newval) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register, |
| /*result as bool*/ true); |
| %} |
| |
| enc_class riscv_enc_cmpxchgn(iRegINoSp res, memory mem, iRegI oldval, iRegI newval) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register, |
| /*result as bool*/ true); |
| %} |
| |
| enc_class riscv_enc_cmpxchg(iRegINoSp res, memory mem, iRegL oldval, iRegL newval) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register, |
| /*result as bool*/ true); |
| %} |
| |
| enc_class riscv_enc_cmpxchgw_acq(iRegINoSp res, memory mem, iRegI oldval, iRegI newval) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register, |
| /*result as bool*/ true); |
| %} |
| |
| enc_class riscv_enc_cmpxchgn_acq(iRegINoSp res, memory mem, iRegI oldval, iRegI newval) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register, |
| /*result as bool*/ true); |
| %} |
| |
| enc_class riscv_enc_cmpxchg_acq(iRegINoSp res, memory mem, iRegL oldval, iRegL newval) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register, |
| /*result as bool*/ true); |
| %} |
| |
| // compare and branch instruction encodings |
| |
| enc_class riscv_enc_j(label lbl) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Label* L = $lbl$$label; |
| __ j(*L); |
| %} |
| |
| enc_class riscv_enc_far_cmpULtGe_imm0_branch(cmpOpULtGe cmp, iRegIorL op1, label lbl) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Label* L = $lbl$$label; |
| switch ($cmp$$cmpcode) { |
| case(BoolTest::ge): |
| __ j(*L); |
| break; |
| case(BoolTest::lt): |
| break; |
| default: |
| Unimplemented(); |
| } |
| %} |
| |
| // call instruction encodings |
| |
| enc_class riscv_enc_partial_subtype_check(iRegP sub, iRegP super, iRegP temp, iRegP result) %{ |
| Register sub_reg = as_Register($sub$$reg); |
| Register super_reg = as_Register($super$$reg); |
| Register temp_reg = as_Register($temp$$reg); |
| Register result_reg = as_Register($result$$reg); |
| Register cr_reg = t1; |
| |
| Label miss; |
| Label done; |
| C2_MacroAssembler _masm(&cbuf); |
| __ check_klass_subtype_slow_path(sub_reg, super_reg, temp_reg, result_reg, |
| NULL, &miss); |
| if ($primary) { |
| __ mv(result_reg, zr); |
| } else { |
| __ mv(cr_reg, zr); |
| __ j(done); |
| } |
| |
| __ bind(miss); |
| if (!$primary) { |
| __ mv(cr_reg, 1); |
| } |
| |
| __ bind(done); |
| %} |
| |
| enc_class riscv_enc_java_static_call(method meth) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Assembler::IncompressibleRegion ir(&_masm); // Fixed length: see ret_addr_offset |
| |
| address addr = (address)$meth$$method; |
| address call = NULL; |
| assert_cond(addr != NULL); |
| if (!_method) { |
| // A call to a runtime wrapper, e.g. new, new_typeArray_Java, uncommon_trap. |
| call = __ trampoline_call(Address(addr, relocInfo::runtime_call_type)); |
| if (call == NULL) { |
| ciEnv::current()->record_failure("CodeCache is full"); |
| return; |
| } |
| } else if (_method->intrinsic_id() == vmIntrinsicID::_ensureMaterializedForStackWalk) { |
| // The NOP here is purely to ensure that eliding a call to |
| // JVM_EnsureMaterializedForStackWalk doesn't change the code size. |
| __ nop(); |
| __ block_comment("call JVM_EnsureMaterializedForStackWalk (elided)"); |
| } 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); |
| call = __ trampoline_call(Address(addr, rspec)); |
| if (call == NULL) { |
| ciEnv::current()->record_failure("CodeCache is full"); |
| return; |
| } |
| |
| if (CodeBuffer::supports_shared_stubs() && _method->can_be_statically_bound()) { |
| // Calls of the same statically bound method can share |
| // a stub to the interpreter. |
| cbuf.shared_stub_to_interp_for(_method, call - cbuf.insts_begin()); |
| } else { |
| // Emit stub for static call |
| address stub = CompiledStaticCall::emit_to_interp_stub(cbuf, call); |
| if (stub == NULL) { |
| ciEnv::current()->record_failure("CodeCache is full"); |
| return; |
| } |
| } |
| } |
| |
| __ post_call_nop(); |
| %} |
| |
| enc_class riscv_enc_java_dynamic_call(method meth) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Assembler::IncompressibleRegion ir(&_masm); // Fixed length: see ret_addr_offset |
| int method_index = resolved_method_index(cbuf); |
| address call = __ ic_call((address)$meth$$method, method_index); |
| if (call == NULL) { |
| ciEnv::current()->record_failure("CodeCache is full"); |
| return; |
| } |
| |
| __ post_call_nop(); |
| %} |
| |
| enc_class riscv_enc_call_epilog() %{ |
| C2_MacroAssembler _masm(&cbuf); |
| if (VerifyStackAtCalls) { |
| // Check that stack depth is unchanged: find majik cookie on stack |
| __ call_Unimplemented(); |
| } |
| %} |
| |
| enc_class riscv_enc_java_to_runtime(method meth) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Assembler::IncompressibleRegion ir(&_masm); // Fixed length: see ret_addr_offset |
| |
| // some calls to generated routines (arraycopy code) are scheduled |
| // by C2 as runtime calls. if so we can call them using a jr (they |
| // will be in a reachable segment) otherwise we have to use a jalr |
| // which loads the absolute address into a register. |
| address entry = (address)$meth$$method; |
| CodeBlob *cb = CodeCache::find_blob(entry); |
| if (cb != NULL) { |
| address call = __ trampoline_call(Address(entry, relocInfo::runtime_call_type)); |
| if (call == NULL) { |
| ciEnv::current()->record_failure("CodeCache is full"); |
| return; |
| } |
| __ post_call_nop(); |
| } else { |
| Label retaddr; |
| __ la(t1, retaddr); |
| __ la(t0, RuntimeAddress(entry)); |
| // Leave a breadcrumb for JavaFrameAnchor::capture_last_Java_pc() |
| __ addi(sp, sp, -2 * wordSize); |
| __ sd(t1, Address(sp, wordSize)); |
| __ jalr(t0); |
| __ bind(retaddr); |
| __ post_call_nop(); |
| __ addi(sp, sp, 2 * wordSize); |
| } |
| %} |
| |
| // arithmetic encodings |
| |
| enc_class riscv_enc_divw(iRegI dst, iRegI src1, iRegI src2) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register dst_reg = as_Register($dst$$reg); |
| Register src1_reg = as_Register($src1$$reg); |
| Register src2_reg = as_Register($src2$$reg); |
| __ corrected_idivl(dst_reg, src1_reg, src2_reg, false); |
| %} |
| |
| enc_class riscv_enc_div(iRegI dst, iRegI src1, iRegI src2) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register dst_reg = as_Register($dst$$reg); |
| Register src1_reg = as_Register($src1$$reg); |
| Register src2_reg = as_Register($src2$$reg); |
| __ corrected_idivq(dst_reg, src1_reg, src2_reg, false); |
| %} |
| |
| enc_class riscv_enc_modw(iRegI dst, iRegI src1, iRegI src2) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register dst_reg = as_Register($dst$$reg); |
| Register src1_reg = as_Register($src1$$reg); |
| Register src2_reg = as_Register($src2$$reg); |
| __ corrected_idivl(dst_reg, src1_reg, src2_reg, true); |
| %} |
| |
| enc_class riscv_enc_mod(iRegI dst, iRegI src1, iRegI src2) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register dst_reg = as_Register($dst$$reg); |
| Register src1_reg = as_Register($src1$$reg); |
| Register src2_reg = as_Register($src2$$reg); |
| __ corrected_idivq(dst_reg, src1_reg, src2_reg, true); |
| %} |
| |
| enc_class riscv_enc_tail_call(iRegP jump_target) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register target_reg = as_Register($jump_target$$reg); |
| __ jr(target_reg); |
| %} |
| |
| enc_class riscv_enc_tail_jmp(iRegP jump_target) %{ |
| C2_MacroAssembler _masm(&cbuf); |
| Register target_reg = as_Register($jump_target$$reg); |
| // exception oop should be in x10 |
| // ret addr has been popped into ra |
| // callee expects it in x13 |
| __ mv(x13, ra); |
| __ jr(target_reg); |
| %} |
| |
| enc_class riscv_enc_rethrow() %{ |
| C2_MacroAssembler _masm(&cbuf); |
| __ far_jump(RuntimeAddress(OptoRuntime::rethrow_stub())); |
| %} |
| |
| enc_class riscv_enc_ret() %{ |
| C2_MacroAssembler _masm(&cbuf); |
| __ ret(); |
| %} |
| |
| %} |
| |
| //----------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 OptoReg::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, even 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 |
| // | +--------+----> OptoReg::stack0(), even 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 necessary 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; |
| // (the latter is true on Intel but is it false on RISCV?) |
| // 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. |
| // Note 4: For I2C adapters, the incoming FP may not meet the minimum stack |
| // alignment. Region 11, pad1, may be dynamically extended so that |
| // SP meets the minimum alignment. |
| |
| frame %{ |
| // These three registers define part of the calling convention |
| // between compiled code and the interpreter. |
| |
| // Inline Cache Register or methodOop for I2C. |
| inline_cache_reg(R31); |
| |
| // Optional: name the operand used by cisc-spilling to access [stack_pointer + offset] |
| cisc_spilling_operand_name(indOffset); |
| |
| // Number of stack slots consumed by locking an object |
| // generate Compile::sync_stack_slots |
| // VMRegImpl::slots_per_word = wordSize / stack_slot_size = 8 / 4 = 2 |
| sync_stack_slots(1 * VMRegImpl::slots_per_word); |
| |
| // Compiled code's Frame Pointer |
| frame_pointer(R2); |
| |
| // Interpreter stores its frame pointer in a register which is |
| // stored to the stack by I2CAdaptors. |
| // I2CAdaptors convert from interpreted java to compiled java. |
| interpreter_frame_pointer(R8); |
| |
| // Stack alignment requirement |
| stack_alignment(StackAlignmentInBytes); // Alignment size in bytes (128-bit -> 16 bytes) |
| |
| // 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. |
| varargs_C_out_slots_killed(frame::arg_reg_save_area_bytes / BytesPerInt); |
| |
| // 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. |
| // Ret Addr is on stack in slot 0 if no locks or verification or alignment. |
| // Otherwise, it is above the locks and verification slot and alignment word |
| // TODO this may well be correct but need to check why that - 2 is there |
| // ppc port uses 0 but we definitely need to allow for fixed_slots |
| // which folds in the space used for monitors |
| return_addr(STACK - 2 + |
| align_up((Compile::current()->in_preserve_stack_slots() + |
| Compile::current()->fixed_slots()), |
| stack_alignment_in_slots())); |
| |
| // Location of compiled Java return values. Same as C for now. |
| return_value |
| %{ |
| assert(ideal_reg >= Op_RegI && ideal_reg <= Op_RegL, |
| "only return normal values"); |
| |
| static const int lo[Op_RegL + 1] = { // enum name |
| 0, // Op_Node |
| 0, // Op_Set |
| R10_num, // Op_RegN |
| R10_num, // Op_RegI |
| R10_num, // Op_RegP |
| F10_num, // Op_RegF |
| F10_num, // Op_RegD |
| R10_num // Op_RegL |
| }; |
| |
| static const int hi[Op_RegL + 1] = { // enum name |
| 0, // Op_Node |
| 0, // Op_Set |
| OptoReg::Bad, // Op_RegN |
| OptoReg::Bad, // Op_RegI |
| R10_H_num, // Op_RegP |
| OptoReg::Bad, // Op_RegF |
| F10_H_num, // Op_RegD |
| R10_H_num // Op_RegL |
| }; |
| |
| return OptoRegPair(hi[ideal_reg], lo[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) |
| ins_attrib ins_short_branch(0); // Required flag: is this instruction |
| // a non-matching short branch variant |
| // of some long branch? |
| ins_attrib ins_alignment(4); // Required alignment attribute (must |
| // be a power of 2) specifies the |
| // alignment that some part of the |
| // instruction (not necessarily the |
| // start) requires. If > 1, a |
| // compute_padding() function must be |
| // provided for the instruction |
| |
| //----------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---------------------------------------------------- |
| |
| // Integer operands 32 bit |
| // 32 bit immediate |
| operand immI() |
| %{ |
| match(ConI); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 32 bit zero |
| operand immI0() |
| %{ |
| predicate(n->get_int() == 0); |
| match(ConI); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 32 bit unit increment |
| operand immI_1() |
| %{ |
| predicate(n->get_int() == 1); |
| match(ConI); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 32 bit unit decrement |
| operand immI_M1() |
| %{ |
| predicate(n->get_int() == -1); |
| match(ConI); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Unsigned Integer Immediate: 6-bit int, greater than 32 |
| operand uimmI6_ge32() %{ |
| predicate(((unsigned int)(n->get_int()) < 64) && (n->get_int() >= 32)); |
| match(ConI); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| operand immI_le_4() |
| %{ |
| predicate(n->get_int() <= 4); |
| match(ConI); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| operand immI_16() |
| %{ |
| predicate(n->get_int() == 16); |
| match(ConI); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| operand immI_24() |
| %{ |
| predicate(n->get_int() == 24); |
| match(ConI); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| operand immI_31() |
| %{ |
| predicate(n->get_int() == 31); |
| match(ConI); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| operand immI_63() |
| %{ |
| predicate(n->get_int() == 63); |
| match(ConI); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 32 bit integer valid for add immediate |
| operand immIAdd() |
| %{ |
| predicate(Assembler::is_simm12((int64_t)n->get_int())); |
| match(ConI); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 32 bit integer valid for sub immediate |
| operand immISub() |
| %{ |
| predicate(Assembler::is_simm12(-(int64_t)n->get_int())); |
| match(ConI); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 5 bit signed value. |
| operand immI5() |
| %{ |
| predicate(n->get_int() <= 15 && n->get_int() >= -16); |
| match(ConI); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 5 bit signed value (simm5) |
| operand immL5() |
| %{ |
| predicate(n->get_long() <= 15 && n->get_long() >= -16); |
| match(ConL); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Integer operands 64 bit |
| // 64 bit immediate |
| operand immL() |
| %{ |
| match(ConL); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 64 bit zero |
| operand immL0() |
| %{ |
| predicate(n->get_long() == 0); |
| match(ConL); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Pointer operands |
| // Pointer Immediate |
| operand immP() |
| %{ |
| match(ConP); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // NULL Pointer Immediate |
| operand immP0() |
| %{ |
| predicate(n->get_ptr() == 0); |
| match(ConP); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Pointer Immediate One |
| // this is used in object initialization (initial object header) |
| operand immP_1() |
| %{ |
| predicate(n->get_ptr() == 1); |
| match(ConP); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Card Table Byte Map Base |
| operand immByteMapBase() |
| %{ |
| // Get base of card map |
| predicate(BarrierSet::barrier_set()->is_a(BarrierSet::CardTableBarrierSet) && |
| (CardTable::CardValue*)n->get_ptr() == |
| ((CardTableBarrierSet*)(BarrierSet::barrier_set()))->card_table()->byte_map_base()); |
| match(ConP); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Int Immediate: low 16-bit mask |
| operand immI_16bits() |
| %{ |
| predicate(n->get_int() == 0xFFFF); |
| match(ConI); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| operand immIpowerOf2() %{ |
| predicate(is_power_of_2((juint)(n->get_int()))); |
| match(ConI); |
| 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); |
| %} |
| |
| // 64 bit unit decrement |
| operand immL_M1() |
| %{ |
| predicate(n->get_long() == -1); |
| match(ConL); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| |
| // 32 bit offset of pc in thread anchor |
| |
| operand immL_pc_off() |
| %{ |
| predicate(n->get_long() == in_bytes(JavaThread::frame_anchor_offset()) + |
| in_bytes(JavaFrameAnchor::last_Java_pc_offset())); |
| match(ConL); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 64 bit integer valid for add immediate |
| operand immLAdd() |
| %{ |
| predicate(Assembler::is_simm12(n->get_long())); |
| match(ConL); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // 64 bit integer valid for sub immediate |
| operand immLSub() |
| %{ |
| predicate(Assembler::is_simm12(-(n->get_long()))); |
| match(ConL); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Narrow pointer operands |
| // Narrow Pointer Immediate |
| operand immN() |
| %{ |
| match(ConN); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Narrow NULL Pointer Immediate |
| operand immN0() |
| %{ |
| predicate(n->get_narrowcon() == 0); |
| match(ConN); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| operand immNKlass() |
| %{ |
| match(ConNKlass); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Float and Double operands |
| // Double Immediate |
| operand immD() |
| %{ |
| match(ConD); |
| op_cost(0); |
| 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(0); |
| 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); |
| %} |
| |
| operand immIOffset() |
| %{ |
| predicate(Assembler::is_simm12(n->get_int())); |
| match(ConI); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| operand immLOffset() |
| %{ |
| predicate(Assembler::is_simm12(n->get_long())); |
| match(ConL); |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Scale values |
| operand immIScale() |
| %{ |
| predicate(1 <= n->get_int() && (n->get_int() <= 3)); |
| match(ConI); |
| |
| op_cost(0); |
| format %{ %} |
| interface(CONST_INTER); |
| %} |
| |
| // Integer 32 bit Register Operands |
| operand iRegI() |
| %{ |
| constraint(ALLOC_IN_RC(any_reg32)); |
| match(RegI); |
| match(iRegINoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Integer 32 bit Register not Special |
| operand iRegINoSp() |
| %{ |
| constraint(ALLOC_IN_RC(no_special_reg32)); |
| match(RegI); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Register R10 only |
| operand iRegI_R10() |
| %{ |
| constraint(ALLOC_IN_RC(int_r10_reg)); |
| match(RegI); |
| match(iRegINoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Register R12 only |
| operand iRegI_R12() |
| %{ |
| constraint(ALLOC_IN_RC(int_r12_reg)); |
| match(RegI); |
| match(iRegINoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Register R13 only |
| operand iRegI_R13() |
| %{ |
| constraint(ALLOC_IN_RC(int_r13_reg)); |
| match(RegI); |
| match(iRegINoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Register R14 only |
| operand iRegI_R14() |
| %{ |
| constraint(ALLOC_IN_RC(int_r14_reg)); |
| match(RegI); |
| match(iRegINoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Integer 64 bit Register Operands |
| operand iRegL() |
| %{ |
| constraint(ALLOC_IN_RC(any_reg)); |
| match(RegL); |
| match(iRegLNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Integer 64 bit Register not Special |
| operand iRegLNoSp() |
| %{ |
| constraint(ALLOC_IN_RC(no_special_reg)); |
| match(RegL); |
| match(iRegL_R10); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Long 64 bit Register R28 only |
| operand iRegL_R28() |
| %{ |
| constraint(ALLOC_IN_RC(r28_reg)); |
| match(RegL); |
| match(iRegLNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Long 64 bit Register R29 only |
| operand iRegL_R29() |
| %{ |
| constraint(ALLOC_IN_RC(r29_reg)); |
| match(RegL); |
| match(iRegLNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Long 64 bit Register R30 only |
| operand iRegL_R30() |
| %{ |
| constraint(ALLOC_IN_RC(r30_reg)); |
| match(RegL); |
| match(iRegLNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Pointer Register Operands |
| // Pointer Register |
| operand iRegP() |
| %{ |
| constraint(ALLOC_IN_RC(ptr_reg)); |
| match(RegP); |
| match(iRegPNoSp); |
| match(iRegP_R10); |
| match(iRegP_R15); |
| match(javaThread_RegP); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Pointer 64 bit Register not Special |
| operand iRegPNoSp() |
| %{ |
| constraint(ALLOC_IN_RC(no_special_ptr_reg)); |
| match(RegP); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand iRegP_R10() |
| %{ |
| constraint(ALLOC_IN_RC(r10_reg)); |
| match(RegP); |
| // match(iRegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Pointer 64 bit Register R11 only |
| operand iRegP_R11() |
| %{ |
| constraint(ALLOC_IN_RC(r11_reg)); |
| match(RegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand iRegP_R12() |
| %{ |
| constraint(ALLOC_IN_RC(r12_reg)); |
| match(RegP); |
| // match(iRegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Pointer 64 bit Register R13 only |
| operand iRegP_R13() |
| %{ |
| constraint(ALLOC_IN_RC(r13_reg)); |
| match(RegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand iRegP_R14() |
| %{ |
| constraint(ALLOC_IN_RC(r14_reg)); |
| match(RegP); |
| // match(iRegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand iRegP_R15() |
| %{ |
| constraint(ALLOC_IN_RC(r15_reg)); |
| match(RegP); |
| // match(iRegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand iRegP_R16() |
| %{ |
| constraint(ALLOC_IN_RC(r16_reg)); |
| match(RegP); |
| // match(iRegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Pointer 64 bit Register R28 only |
| operand iRegP_R28() |
| %{ |
| constraint(ALLOC_IN_RC(r28_reg)); |
| match(RegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Pointer 64 bit Register R30 only |
| operand iRegP_R30() |
| %{ |
| constraint(ALLOC_IN_RC(r30_reg)); |
| match(RegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Pointer 64 bit Register R31 only |
| operand iRegP_R31() |
| %{ |
| constraint(ALLOC_IN_RC(r31_reg)); |
| match(RegP); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Pointer Register Operands |
| // Narrow Pointer Register |
| operand iRegN() |
| %{ |
| constraint(ALLOC_IN_RC(any_reg32)); |
| match(RegN); |
| match(iRegNNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Integer 64 bit Register not Special |
| operand iRegNNoSp() |
| %{ |
| constraint(ALLOC_IN_RC(no_special_reg32)); |
| match(RegN); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Long 64 bit Register R10 only |
| operand iRegL_R10() |
| %{ |
| constraint(ALLOC_IN_RC(r10_reg)); |
| match(RegL); |
| match(iRegLNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Float Register |
| // Float register operands |
| operand fRegF() |
| %{ |
| constraint(ALLOC_IN_RC(float_reg)); |
| match(RegF); |
| |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Double Register |
| // Double register operands |
| operand fRegD() |
| %{ |
| constraint(ALLOC_IN_RC(double_reg)); |
| match(RegD); |
| |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Generic vector class. This will be used for |
| // all vector operands. |
| operand vReg() |
| %{ |
| constraint(ALLOC_IN_RC(vectora_reg)); |
| match(VecA); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V1() |
| %{ |
| constraint(ALLOC_IN_RC(v1_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V2() |
| %{ |
| constraint(ALLOC_IN_RC(v2_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V3() |
| %{ |
| constraint(ALLOC_IN_RC(v3_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V4() |
| %{ |
| constraint(ALLOC_IN_RC(v4_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V5() |
| %{ |
| constraint(ALLOC_IN_RC(v5_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V6() |
| %{ |
| constraint(ALLOC_IN_RC(v6_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V7() |
| %{ |
| constraint(ALLOC_IN_RC(v7_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V8() |
| %{ |
| constraint(ALLOC_IN_RC(v8_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V9() |
| %{ |
| constraint(ALLOC_IN_RC(v9_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V10() |
| %{ |
| constraint(ALLOC_IN_RC(v10_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vReg_V11() |
| %{ |
| constraint(ALLOC_IN_RC(v11_reg)); |
| match(VecA); |
| match(vReg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| operand vRegMask() |
| %{ |
| constraint(ALLOC_IN_RC(vmask_reg)); |
| match(RegVectMask); |
| match(vRegMask_V0); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // The mask value used to control execution of a masked |
| // vector instruction is always supplied by vector register v0. |
| operand vRegMask_V0() |
| %{ |
| constraint(ALLOC_IN_RC(vmask_reg_v0)); |
| match(RegVectMask); |
| match(vRegMask); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| // Java Thread Register |
| operand javaThread_RegP(iRegP reg) |
| %{ |
| constraint(ALLOC_IN_RC(java_thread_reg)); // java_thread_reg |
| match(reg); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| //----------Memory Operands---------------------------------------------------- |
| // RISCV has only base_plus_offset and literal address mode, so no need to use |
| // index and scale. Here set index as 0xffffffff and scale as 0x0. |
| operand indirect(iRegP reg) |
| %{ |
| constraint(ALLOC_IN_RC(ptr_reg)); |
| match(reg); |
| op_cost(0); |
| format %{ "[$reg]" %} |
| interface(MEMORY_INTER) %{ |
| base($reg); |
| index(0xffffffff); |
| scale(0x0); |
| disp(0x0); |
| %} |
| %} |
| |
| operand indOffI(iRegP reg, immIOffset off) |
| %{ |
| constraint(ALLOC_IN_RC(ptr_reg)); |
| match(AddP reg off); |
| op_cost(0); |
| format %{ "[$reg, $off]" %} |
| interface(MEMORY_INTER) %{ |
| base($reg); |
| index(0xffffffff); |
| scale(0x0); |
| disp($off); |
| %} |
| %} |
| |
| operand indOffL(iRegP reg, immLOffset off) |
| %{ |
| constraint(ALLOC_IN_RC(ptr_reg)); |
| match(AddP reg off); |
| op_cost(0); |
| format %{ "[$reg, $off]" %} |
| interface(MEMORY_INTER) %{ |
| base($reg); |
| index(0xffffffff); |
| scale(0x0); |
| disp($off); |
| %} |
| %} |
| |
| operand indirectN(iRegN reg) |
| %{ |
| predicate(CompressedOops::shift() == 0); |
| constraint(ALLOC_IN_RC(ptr_reg)); |
| match(DecodeN reg); |
| op_cost(0); |
| format %{ "[$reg]\t# narrow" %} |
| interface(MEMORY_INTER) %{ |
| base($reg); |
| index(0xffffffff); |
| scale(0x0); |
| disp(0x0); |
| %} |
| %} |
| |
| operand indOffIN(iRegN reg, immIOffset off) |
| %{ |
| predicate(CompressedOops::shift() == 0); |
| constraint(ALLOC_IN_RC(ptr_reg)); |
| match(AddP (DecodeN reg) off); |
| op_cost(0); |
| format %{ "[$reg, $off]\t# narrow" %} |
| interface(MEMORY_INTER) %{ |
| base($reg); |
| index(0xffffffff); |
| scale(0x0); |
| disp($off); |
| %} |
| %} |
| |
| operand indOffLN(iRegN reg, immLOffset off) |
| %{ |
| predicate(CompressedOops::shift() == 0); |
| constraint(ALLOC_IN_RC(ptr_reg)); |
| match(AddP (DecodeN reg) off); |
| op_cost(0); |
| format %{ "[$reg, $off]\t# narrow" %} |
| interface(MEMORY_INTER) %{ |
| base($reg); |
| index(0xffffffff); |
| scale(0x0); |
| disp($off); |
| %} |
| %} |
| |
| // RISCV opto stubs need to write to the pc slot in the thread anchor |
| operand thread_anchor_pc(javaThread_RegP reg, immL_pc_off off) |
| %{ |
| constraint(ALLOC_IN_RC(ptr_reg)); |
| match(AddP reg off); |
| op_cost(0); |
| format %{ "[$reg, $off]" %} |
| interface(MEMORY_INTER) %{ |
| base($reg); |
| index(0xffffffff); |
| scale(0x0); |
| disp($off); |
| %} |
| %} |
| |
| |
| //----------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)); |
| // No match rule because this operand is only generated in matching |
| // match(RegI); |
| format %{ "[$reg]" %} |
| interface(MEMORY_INTER) %{ |
| base(0x02); // RSP |
| index(0xffffffff); // No Index |
| scale(0x0); // No Scale |
| disp($reg); // Stack Offset |
| %} |
| %} |
| |
| operand stackSlotF(sRegF reg) |
| %{ |
| constraint(ALLOC_IN_RC(stack_slots)); |
| // No match rule because this operand is only generated in matching |
| // match(RegF); |
| format %{ "[$reg]" %} |
| interface(MEMORY_INTER) %{ |
| base(0x02); // RSP |
| index(0xffffffff); // No Index |
| scale(0x0); // No Scale |
| disp($reg); // Stack Offset |
| %} |
| %} |
| |
| operand stackSlotD(sRegD reg) |
| %{ |
| constraint(ALLOC_IN_RC(stack_slots)); |
| // No match rule because this operand is only generated in matching |
| // match(RegD); |
| format %{ "[$reg]" %} |
| interface(MEMORY_INTER) %{ |
| base(0x02); // RSP |
| index(0xffffffff); // No Index |
| scale(0x0); // No Scale |
| disp($reg); // Stack Offset |
| %} |
| %} |
| |
| operand stackSlotL(sRegL reg) |
| %{ |
| constraint(ALLOC_IN_RC(stack_slots)); |
| // No match rule because this operand is only generated in matching |
| // match(RegL); |
| format %{ "[$reg]" %} |
| interface(MEMORY_INTER) %{ |
| base(0x02); // RSP |
| index(0xffffffff); // No Index |
| scale(0x0); // No Scale |
| disp($reg); // Stack Offset |
| %} |
| %} |
| |
| // Special operand allowing long args to int ops to be truncated for free |
| |
| operand iRegL2I(iRegL reg) %{ |
| |
| op_cost(0); |
| |
| match(ConvL2I reg); |
| |
| format %{ "l2i($reg)" %} |
| |
| interface(REG_INTER) |
| %} |
| |
| |
| // Comparison Operands |
| // 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. |
| |
| |
| // used for signed integral comparisons and fp comparisons |
| operand cmpOp() |
| %{ |
| match(Bool); |
| |
| format %{ "" %} |
| |
| // the values in interface derives from struct BoolTest::mask |
| interface(COND_INTER) %{ |
| equal(0x0, "eq"); |
| greater(0x1, "gt"); |
| overflow(0x2, "overflow"); |
| less(0x3, "lt"); |
| not_equal(0x4, "ne"); |
| less_equal(0x5, "le"); |
| no_overflow(0x6, "no_overflow"); |
| greater_equal(0x7, "ge"); |
| %} |
| %} |
| |
| // used for unsigned integral comparisons |
| operand cmpOpU() |
| %{ |
| match(Bool); |
| |
| format %{ "" %} |
| // the values in interface derives from struct BoolTest::mask |
| interface(COND_INTER) %{ |
| equal(0x0, "eq"); |
| greater(0x1, "gtu"); |
| overflow(0x2, "overflow"); |
| less(0x3, "ltu"); |
| not_equal(0x4, "ne"); |
| less_equal(0x5, "leu"); |
| no_overflow(0x6, "no_overflow"); |
| greater_equal(0x7, "geu"); |
| %} |
| %} |
| |
| // used for certain integral comparisons which can be |
| // converted to bxx instructions |
| operand cmpOpEqNe() |
| %{ |
| match(Bool); |
| op_cost(0); |
| predicate(n->as_Bool()->_test._test == BoolTest::ne || |
| n->as_Bool()->_test._test == BoolTest::eq); |
| |
| format %{ "" %} |
| interface(COND_INTER) %{ |
| equal(0x0, "eq"); |
| greater(0x1, "gt"); |
| overflow(0x2, "overflow"); |
| less(0x3, "lt"); |
| not_equal(0x4, "ne"); |
| less_equal(0x5, "le"); |
| no_overflow(0x6, "no_overflow"); |
| greater_equal(0x7, "ge"); |
| %} |
| %} |
| |
| operand cmpOpULtGe() |
| %{ |
| match(Bool); |
| op_cost(0); |
| predicate(n->as_Bool()->_test._test == BoolTest::lt || |
| n->as_Bool()->_test._test == BoolTest::ge); |
| |
| format %{ "" %} |
| interface(COND_INTER) %{ |
| equal(0x0, "eq"); |
| greater(0x1, "gtu"); |
| overflow(0x2, "overflow"); |
| less(0x3, "ltu"); |
| not_equal(0x4, "ne"); |
| less_equal(0x5, "leu"); |
| no_overflow(0x6, "no_overflow"); |
| greater_equal(0x7, "geu"); |
| %} |
| %} |
| |
| operand cmpOpUEqNeLeGt() |
| %{ |
| match(Bool); |
| op_cost(0); |
| predicate(n->as_Bool()->_test._test == BoolTest::ne || |
| n->as_Bool()->_test._test == BoolTest::eq || |
| n->as_Bool()->_test._test == BoolTest::le || |
| n->as_Bool()->_test._test == BoolTest::gt); |
| |
| format %{ "" %} |
| interface(COND_INTER) %{ |
| equal(0x0, "eq"); |
| greater(0x1, "gtu"); |
| overflow(0x2, "overflow"); |
| less(0x3, "ltu"); |
| not_equal(0x4, "ne"); |
| less_equal(0x5, "leu"); |
| no_overflow(0x6, "no_overflow"); |
| greater_equal(0x7, "geu"); |
| %} |
| %} |
| |
| |
| // Flags register, used as output of compare logic |
| operand rFlagsReg() |
| %{ |
| constraint(ALLOC_IN_RC(reg_flags)); |
| match(RegFlags); |
| |
| op_cost(0); |
| format %{ "RFLAGS" %} |
| interface(REG_INTER); |
| %} |
| |
| // Special Registers |
| |
| // Method Register |
| operand inline_cache_RegP(iRegP reg) |
| %{ |
| constraint(ALLOC_IN_RC(method_reg)); // inline_cache_reg |
| match(reg); |
| match(iRegPNoSp); |
| op_cost(0); |
| format %{ %} |
| interface(REG_INTER); |
| %} |
| |
| //----------OPERAND CLASSES---------------------------------------------------- |
| // Operand Classes are groups of operands that are used as 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. |
| |
| // memory is used to define read/write location for load/store |
| // instruction defs. we can turn a memory op into an Address |
| |
| opclass memory(indirect, indOffI, indOffL, indirectN, indOffIN, indOffLN); |
| |
| // iRegIorL2I is used for src inputs in rules for 32 bit int (I) |
| // operations. it allows the src to be either an iRegI or a (ConvL2I |
| // iRegL). in the latter case the l2i normally planted for a ConvL2I |
| // can be elided because the 32-bit instruction will just employ the |
| // lower 32 bits anyway. |
| // |
| // n.b. this does not elide all L2I conversions. if the truncated |
| // value is consumed by more than one operation then the ConvL2I |
| // cannot be bundled into the consuming nodes so an l2i gets planted |
| // (actually an addiw $dst, $src, 0) and the downstream instructions |
| // consume the result of the L2I as an iRegI input. That's a shame since |
| // the addiw is actually redundant but its not too costly. |
| |
| opclass iRegIorL2I(iRegI, iRegL2I); |
| opclass iRegIorL(iRegI, iRegL); |
| opclass iRegNorP(iRegN, iRegP); |
| opclass iRegILNP(iRegI, iRegL, iRegN, iRegP); |
| opclass iRegILNPNoSp(iRegINoSp, iRegLNoSp, iRegNNoSp, iRegPNoSp); |
| opclass immIorL(immI, immL); |
| |
| //----------PIPELINE----------------------------------------------------------- |
| // Rules which define the behavior of the target architectures pipeline. |
| |
| // For specific pipelines, e.g. generic RISC-V, define the stages of that pipeline |
| //pipe_desc(ID, EX, MEM, WR); |
| #define ID S0 |
| #define EX S1 |
| #define MEM S2 |
| #define WR S3 |
| |
| // Integer ALU reg operation |
| pipeline %{ |
| |
| attributes %{ |
| // RISC-V instructions are of fixed length |
| fixed_size_instructions; // Fixed size instructions TODO does |
| max_instructions_per_bundle = 2; // Generic RISC-V 1, Sifive Series 7 2 |
| // RISC-V instructions come in 32-bit word units |
| instruction_unit_size = 4; // An instruction is 4 bytes long |
| instruction_fetch_unit_size = 64; // The processor fetches one line |
| instruction_fetch_units = 1; // of 64 bytes |
| |
| // List of nop instructions |
| nops( MachNop ); |
| %} |
| |
| // We don't use an actual pipeline model so don't care about resources |
| // or description. we do use pipeline classes to introduce fixed |
| // latencies |
| |
| //----------RESOURCES---------------------------------------------------------- |
| // Resources are the functional units available to the machine |
| |
| // Generic RISC-V pipeline |
| // 1 decoder |
| // 1 instruction decoded per cycle |
| // 1 load/store ops per cycle, 1 branch, 1 FPU |
| // 1 mul, 1 div |
| |
| resources ( DECODE, |
| ALU, |
| MUL, |
| DIV, |
| BRANCH, |
| LDST, |
| FPU); |
| |
| //----------PIPELINE DESCRIPTION----------------------------------------------- |
| // Pipeline Description specifies the stages in the machine's pipeline |
| |
| // Define the pipeline as a generic 6 stage pipeline |
| pipe_desc(S0, S1, S2, S3, S4, S5); |
| |
| //----------PIPELINE CLASSES--------------------------------------------------- |
| // Pipeline Classes describe the stages in which input and output are |
| // referenced by the hardware pipeline. |
| |
| pipe_class fp_dop_reg_reg_s(fRegF dst, fRegF src1, fRegF src2) |
| %{ |
| single_instruction; |
| src1 : S1(read); |
| src2 : S2(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_dop_reg_reg_d(fRegD dst, fRegD src1, fRegD src2) |
| %{ |
| src1 : S1(read); |
| src2 : S2(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_uop_s(fRegF dst, fRegF src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_uop_d(fRegD dst, fRegD src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_d2f(fRegF dst, fRegD src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_f2d(fRegD dst, fRegF src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_f2i(iRegINoSp dst, fRegF src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_f2l(iRegLNoSp dst, fRegF src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_i2f(fRegF dst, iRegIorL2I src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_l2f(fRegF dst, iRegL src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_d2i(iRegINoSp dst, fRegD src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_d2l(iRegLNoSp dst, fRegD src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_i2d(fRegD dst, iRegIorL2I src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_l2d(fRegD dst, iRegIorL2I src) |
| %{ |
| single_instruction; |
| src : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_div_s(fRegF dst, fRegF src1, fRegF src2) |
| %{ |
| single_instruction; |
| src1 : S1(read); |
| src2 : S2(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_div_d(fRegD dst, fRegD src1, fRegD src2) |
| %{ |
| single_instruction; |
| src1 : S1(read); |
| src2 : S2(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_sqrt_s(fRegF dst, fRegF src1, fRegF src2) |
| %{ |
| single_instruction; |
| src1 : S1(read); |
| src2 : S2(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_sqrt_d(fRegD dst, fRegD src1, fRegD src2) |
| %{ |
| single_instruction; |
| src1 : S1(read); |
| src2 : S2(read); |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_load_constant_s(fRegF dst) |
| %{ |
| single_instruction; |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_load_constant_d(fRegD dst) |
| %{ |
| single_instruction; |
| dst : S5(write); |
| DECODE : ID; |
| FPU : S5; |
| %} |
| |
| pipe_class fp_load_mem_s(fRegF dst, memory mem) |
| %{ |
| single_instruction; |
| mem : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| pipe_class fp_load_mem_d(fRegD dst, memory mem) |
| %{ |
| single_instruction; |
| mem : S1(read); |
| dst : S5(write); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| pipe_class fp_store_reg_s(fRegF src, memory mem) |
| %{ |
| single_instruction; |
| src : S1(read); |
| mem : S5(write); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| pipe_class fp_store_reg_d(fRegD src, memory mem) |
| %{ |
| single_instruction; |
| src : S1(read); |
| mem : S5(write); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| //------- Integer ALU operations -------------------------- |
| |
| // Integer ALU reg-reg operation |
| // Operands needs in ID, result generated in EX |
| // E.g. ADD Rd, Rs1, Rs2 |
| pipe_class ialu_reg_reg(iRegI dst, iRegI src1, iRegI src2) |
| %{ |
| single_instruction; |
| dst : EX(write); |
| src1 : ID(read); |
| src2 : ID(read); |
| DECODE : ID; |
| ALU : EX; |
| %} |
| |
| // Integer ALU reg operation with constant shift |
| // E.g. SLLI Rd, Rs1, #shift |
| pipe_class ialu_reg_shift(iRegI dst, iRegI src1) |
| %{ |
| single_instruction; |
| dst : EX(write); |
| src1 : ID(read); |
| DECODE : ID; |
| ALU : EX; |
| %} |
| |
| // Integer ALU reg-reg operation with variable shift |
| // both operands must be available in ID |
| // E.g. SLL Rd, Rs1, Rs2 |
| pipe_class ialu_reg_reg_vshift(iRegI dst, iRegI src1, iRegI src2) |
| %{ |
| single_instruction; |
| dst : EX(write); |
| src1 : ID(read); |
| src2 : ID(read); |
| DECODE : ID; |
| ALU : EX; |
| %} |
| |
| // Integer ALU reg operation |
| // E.g. NEG Rd, Rs2 |
| pipe_class ialu_reg(iRegI dst, iRegI src) |
| %{ |
| single_instruction; |
| dst : EX(write); |
| src : ID(read); |
| DECODE : ID; |
| ALU : EX; |
| %} |
| |
| // Integer ALU reg immediate operation |
| // E.g. ADDI Rd, Rs1, #imm |
| pipe_class ialu_reg_imm(iRegI dst, iRegI src1) |
| %{ |
| single_instruction; |
| dst : EX(write); |
| src1 : ID(read); |
| DECODE : ID; |
| ALU : EX; |
| %} |
| |
| // Integer ALU immediate operation (no source operands) |
| // E.g. LI Rd, #imm |
| pipe_class ialu_imm(iRegI dst) |
| %{ |
| single_instruction; |
| dst : EX(write); |
| DECODE : ID; |
| ALU : EX; |
| %} |
| |
| //------- Multiply pipeline operations -------------------- |
| |
| // Multiply reg-reg |
| // E.g. MULW Rd, Rs1, Rs2 |
| pipe_class imul_reg_reg(iRegI dst, iRegI src1, iRegI src2) |
| %{ |
| single_instruction; |
| dst : WR(write); |
| src1 : ID(read); |
| src2 : ID(read); |
| DECODE : ID; |
| MUL : WR; |
| %} |
| |
| // E.g. MUL RD, Rs1, Rs2 |
| pipe_class lmul_reg_reg(iRegL dst, iRegL src1, iRegL src2) |
| %{ |
| single_instruction; |
| fixed_latency(3); // Maximum latency for 64 bit mul |
| dst : WR(write); |
| src1 : ID(read); |
| src2 : ID(read); |
| DECODE : ID; |
| MUL : WR; |
| %} |
| |
| //------- Divide pipeline operations -------------------- |
| |
| // E.g. DIVW Rd, Rs1, Rs2 |
| pipe_class idiv_reg_reg(iRegI dst, iRegI src1, iRegI src2) |
| %{ |
| single_instruction; |
| fixed_latency(8); // Maximum latency for 32 bit divide |
| dst : WR(write); |
| src1 : ID(read); |
| src2 : ID(read); |
| DECODE : ID; |
| DIV : WR; |
| %} |
| |
| // E.g. DIV RD, Rs1, Rs2 |
| pipe_class ldiv_reg_reg(iRegL dst, iRegL src1, iRegL src2) |
| %{ |
| single_instruction; |
| fixed_latency(16); // Maximum latency for 64 bit divide |
| dst : WR(write); |
| src1 : ID(read); |
| src2 : ID(read); |
| DECODE : ID; |
| DIV : WR; |
| %} |
| |
| //------- Load pipeline operations ------------------------ |
| |
| // Load - prefetch |
| // Eg. PREFETCH_W mem |
| pipe_class iload_prefetch(memory mem) |
| %{ |
| single_instruction; |
| mem : ID(read); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| // Load - reg, mem |
| // E.g. LA Rd, mem |
| pipe_class iload_reg_mem(iRegI dst, memory mem) |
| %{ |
| single_instruction; |
| dst : WR(write); |
| mem : ID(read); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| // Load - reg, reg |
| // E.g. LD Rd, Rs |
| pipe_class iload_reg_reg(iRegI dst, iRegI src) |
| %{ |
| single_instruction; |
| dst : WR(write); |
| src : ID(read); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| //------- Store pipeline operations ----------------------- |
| |
| // Store - zr, mem |
| // E.g. SD zr, mem |
| pipe_class istore_mem(memory mem) |
| %{ |
| single_instruction; |
| mem : ID(read); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| // Store - reg, mem |
| // E.g. SD Rs, mem |
| pipe_class istore_reg_mem(iRegI src, memory mem) |
| %{ |
| single_instruction; |
| mem : ID(read); |
| src : EX(read); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| // Store - reg, reg |
| // E.g. SD Rs2, Rs1 |
| pipe_class istore_reg_reg(iRegI dst, iRegI src) |
| %{ |
| single_instruction; |
| dst : ID(read); |
| src : EX(read); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| //------- Control transfer pipeline operations ------------ |
| |
| // Branch |
| pipe_class pipe_branch() |
| %{ |
| single_instruction; |
| DECODE : ID; |
| BRANCH : EX; |
| %} |
| |
| // Branch |
| pipe_class pipe_branch_reg(iRegI src) |
| %{ |
| single_instruction; |
| src : ID(read); |
| DECODE : ID; |
| BRANCH : EX; |
| %} |
| |
| // Compare & Branch |
| // E.g. BEQ Rs1, Rs2, L |
| pipe_class pipe_cmp_branch(iRegI src1, iRegI src2) |
| %{ |
| single_instruction; |
| src1 : ID(read); |
| src2 : ID(read); |
| DECODE : ID; |
| BRANCH : EX; |
| %} |
| |
| // E.g. BEQZ Rs, L |
| pipe_class pipe_cmpz_branch(iRegI src) |
| %{ |
| single_instruction; |
| src : ID(read); |
| DECODE : ID; |
| BRANCH : EX; |
| %} |
| |
| //------- Synchronisation operations ---------------------- |
| // Any operation requiring serialization |
| // E.g. FENCE/Atomic Ops/Load Acquire/Store Release |
| pipe_class pipe_serial() |
| %{ |
| single_instruction; |
| force_serialization; |
| fixed_latency(16); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| pipe_class pipe_slow() |
| %{ |
| instruction_count(10); |
| multiple_bundles; |
| force_serialization; |
| fixed_latency(16); |
| DECODE : ID; |
| LDST : MEM; |
| %} |
| |
| // Empty pipeline class |
| pipe_class pipe_class_empty() |
| %{ |
| single_instruction; |
| fixed_latency(0); |
| %} |
| |
| // Default pipeline class. |
| pipe_class pipe_class_default() |
| %{ |
| single_instruction; |
| fixed_latency(2); |
| %} |
| |
| // Pipeline class for compares. |
| pipe_class pipe_class_compare() |
| %{ |
| single_instruction; |
| fixed_latency(16); |
| %} |
| |
| // Pipeline class for memory operations. |
| pipe_class pipe_class_memory() |
| %{ |
| single_instruction; |
| fixed_latency(16); |
| %} |
| |
| // Pipeline class for call. |
| pipe_class pipe_class_call() |
| %{ |
| single_instruction; |
| fixed_latency(100); |
| %} |
| |
| // Define the class for the Nop node. |
| define %{ |
| MachNop = pipe_class_empty; |
| %} |
| %} |
| //----------INSTRUCTIONS------------------------------------------------------- |
| // |
| // match -- States which machine-independent subtree may be replaced |
| // by this instruction. |
| // ins_cost -- The estimated cost of this instruction is used by instruction |
| // selection to identify a minimum cost tree of machine |
| // instructions that matches a tree of machine-independent |
| // instructions. |
| // format -- A string providing the disassembly for this instruction. |
| // The value of an instruction's operand may be inserted |
| // by referring to it with a '$' prefix. |
| // opcode -- Three instruction opcodes may be provided. These are referred |
| // to within an encode class as $primary, $secondary, and $tertiary |
| // rrspectively. The primary opcode is commonly used to |
| // indicate the type of machine instruction, while secondary |
| // and tertiary are often used for prefix options or addressing |
| // modes. |
| // ins_encode -- A list of encode classes with parameters. The encode class |
| // name must have been defined in an 'enc_class' specification |
| // in the encode section of the architecture description. |
| |
| // ============================================================================ |
| // Memory (Load/Store) Instructions |
| |
| // Load Instructions |
| |
| // Load Byte (8 bit signed) |
| instruct loadB(iRegINoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadB mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lb $dst, $mem\t# byte, #@loadB" %} |
| |
| ins_encode %{ |
| __ lb(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Byte (8 bit signed) into long |
| instruct loadB2L(iRegLNoSp dst, memory mem) |
| %{ |
| match(Set dst (ConvI2L (LoadB mem))); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lb $dst, $mem\t# byte, #@loadB2L" %} |
| |
| ins_encode %{ |
| __ lb(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Byte (8 bit unsigned) |
| instruct loadUB(iRegINoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadUB mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lbu $dst, $mem\t# byte, #@loadUB" %} |
| |
| ins_encode %{ |
| __ lbu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Byte (8 bit unsigned) into long |
| instruct loadUB2L(iRegLNoSp dst, memory mem) |
| %{ |
| match(Set dst (ConvI2L (LoadUB mem))); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lbu $dst, $mem\t# byte, #@loadUB2L" %} |
| |
| ins_encode %{ |
| __ lbu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Short (16 bit signed) |
| instruct loadS(iRegINoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadS mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lh $dst, $mem\t# short, #@loadS" %} |
| |
| ins_encode %{ |
| __ lh(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Short (16 bit signed) into long |
| instruct loadS2L(iRegLNoSp dst, memory mem) |
| %{ |
| match(Set dst (ConvI2L (LoadS mem))); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lh $dst, $mem\t# short, #@loadS2L" %} |
| |
| ins_encode %{ |
| __ lh(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Char (16 bit unsigned) |
| instruct loadUS(iRegINoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadUS mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lhu $dst, $mem\t# short, #@loadUS" %} |
| |
| ins_encode %{ |
| __ lhu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Short/Char (16 bit unsigned) into long |
| instruct loadUS2L(iRegLNoSp dst, memory mem) |
| %{ |
| match(Set dst (ConvI2L (LoadUS mem))); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lhu $dst, $mem\t# short, #@loadUS2L" %} |
| |
| ins_encode %{ |
| __ lhu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Integer (32 bit signed) |
| instruct loadI(iRegINoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadI mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lw $dst, $mem\t# int, #@loadI" %} |
| |
| ins_encode %{ |
| __ lw(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Integer (32 bit signed) into long |
| instruct loadI2L(iRegLNoSp dst, memory mem) |
| %{ |
| match(Set dst (ConvI2L (LoadI mem))); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lw $dst, $mem\t# int, #@loadI2L" %} |
| |
| ins_encode %{ |
| __ lw(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Integer (32 bit unsigned) into long |
| instruct loadUI2L(iRegLNoSp dst, memory mem, immL_32bits mask) |
| %{ |
| match(Set dst (AndL (ConvI2L (LoadI mem)) mask)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lwu $dst, $mem\t# int, #@loadUI2L" %} |
| |
| ins_encode %{ |
| __ lwu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Long (64 bit signed) |
| instruct loadL(iRegLNoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadL mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "ld $dst, $mem\t# int, #@loadL" %} |
| |
| ins_encode %{ |
| __ ld(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Range |
| instruct loadRange(iRegINoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadRange mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lwu $dst, $mem\t# range, #@loadRange" %} |
| |
| ins_encode %{ |
| __ lwu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Pointer |
| instruct loadP(iRegPNoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadP mem)); |
| predicate(n->as_Load()->barrier_data() == 0); |
| |
| ins_cost(LOAD_COST); |
| format %{ "ld $dst, $mem\t# ptr, #@loadP" %} |
| |
| ins_encode %{ |
| __ ld(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Compressed Pointer |
| instruct loadN(iRegNNoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadN mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lwu $dst, $mem\t# loadN, compressed ptr, #@loadN" %} |
| |
| ins_encode %{ |
| __ lwu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Klass Pointer |
| instruct loadKlass(iRegPNoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadKlass mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "ld $dst, $mem\t# class, #@loadKlass" %} |
| |
| ins_encode %{ |
| __ ld(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Narrow Klass Pointer |
| instruct loadNKlass(iRegNNoSp dst, memory mem) |
| %{ |
| match(Set dst (LoadNKlass mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "lwu $dst, $mem\t# loadNKlass, compressed class ptr, #@loadNKlass" %} |
| |
| ins_encode %{ |
| __ lwu(as_Register($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_mem); |
| %} |
| |
| // Load Float |
| instruct loadF(fRegF dst, memory mem) |
| %{ |
| match(Set dst (LoadF mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "flw $dst, $mem\t# float, #@loadF" %} |
| |
| ins_encode %{ |
| __ flw(as_FloatRegister($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(fp_load_mem_s); |
| %} |
| |
| // Load Double |
| instruct loadD(fRegD dst, memory mem) |
| %{ |
| match(Set dst (LoadD mem)); |
| |
| ins_cost(LOAD_COST); |
| format %{ "fld $dst, $mem\t# double, #@loadD" %} |
| |
| ins_encode %{ |
| __ fld(as_FloatRegister($dst$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(fp_load_mem_d); |
| %} |
| |
| // Load Int Constant |
| instruct loadConI(iRegINoSp dst, immI src) |
| %{ |
| match(Set dst src); |
| |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $src\t# int, #@loadConI" %} |
| |
| ins_encode(riscv_enc_mov_imm(dst, src)); |
| |
| ins_pipe(ialu_imm); |
| %} |
| |
| // Load Long Constant |
| instruct loadConL(iRegLNoSp dst, immL src) |
| %{ |
| match(Set dst src); |
| |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $src\t# long, #@loadConL" %} |
| |
| ins_encode(riscv_enc_mov_imm(dst, src)); |
| |
| ins_pipe(ialu_imm); |
| %} |
| |
| // Load Pointer Constant |
| instruct loadConP(iRegPNoSp dst, immP con) |
| %{ |
| match(Set dst con); |
| |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $con\t# ptr, #@loadConP" %} |
| |
| ins_encode(riscv_enc_mov_p(dst, con)); |
| |
| ins_pipe(ialu_imm); |
| %} |
| |
| // Load Null Pointer Constant |
| instruct loadConP0(iRegPNoSp dst, immP0 con) |
| %{ |
| match(Set dst con); |
| |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $con\t# NULL ptr, #@loadConP0" %} |
| |
| ins_encode(riscv_enc_mov_zero(dst)); |
| |
| ins_pipe(ialu_imm); |
| %} |
| |
| // Load Pointer Constant One |
| instruct loadConP1(iRegPNoSp dst, immP_1 con) |
| %{ |
| match(Set dst con); |
| |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $con\t# load ptr constant one, #@loadConP1" %} |
| |
| ins_encode(riscv_enc_mov_p1(dst)); |
| |
| ins_pipe(ialu_imm); |
| %} |
| |
| // Load Byte Map Base Constant |
| instruct loadByteMapBase(iRegPNoSp dst, immByteMapBase con) |
| %{ |
| match(Set dst con); |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $con\t# Byte Map Base, #@loadByteMapBase" %} |
| |
| ins_encode(riscv_enc_mov_byte_map_base(dst)); |
| |
| ins_pipe(ialu_imm); |
| %} |
| |
| // Load Narrow Pointer Constant |
| instruct loadConN(iRegNNoSp dst, immN con) |
| %{ |
| match(Set dst con); |
| |
| ins_cost(ALU_COST * 4); |
| format %{ "mv $dst, $con\t# compressed ptr, #@loadConN" %} |
| |
| ins_encode(riscv_enc_mov_n(dst, con)); |
| |
| ins_pipe(ialu_imm); |
| %} |
| |
| // Load Narrow Null Pointer Constant |
| instruct loadConN0(iRegNNoSp dst, immN0 con) |
| %{ |
| match(Set dst con); |
| |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $con\t# compressed NULL ptr, #@loadConN0" %} |
| |
| ins_encode(riscv_enc_mov_zero(dst)); |
| |
| ins_pipe(ialu_imm); |
| %} |
| |
| // Load Narrow Klass Constant |
| instruct loadConNKlass(iRegNNoSp dst, immNKlass con) |
| %{ |
| match(Set dst con); |
| |
| ins_cost(ALU_COST * 6); |
| format %{ "mv $dst, $con\t# compressed klass ptr, #@loadConNKlass" %} |
| |
| ins_encode(riscv_enc_mov_nk(dst, con)); |
| |
| ins_pipe(ialu_imm); |
| %} |
| |
| // Load Float Constant |
| instruct loadConF(fRegF dst, immF con) %{ |
| match(Set dst con); |
| |
| ins_cost(LOAD_COST); |
| format %{ |
| "flw $dst, [$constantaddress]\t# load from constant table: float=$con, #@loadConF" |
| %} |
| |
| ins_encode %{ |
| __ flw(as_FloatRegister($dst$$reg), $constantaddress($con)); |
| %} |
| |
| ins_pipe(fp_load_constant_s); |
| %} |
| |
| instruct loadConF0(fRegF dst, immF0 con) %{ |
| match(Set dst con); |
| |
| ins_cost(XFER_COST); |
| |
| format %{ "fmv.w.x $dst, zr\t# float, #@loadConF0" %} |
| |
| ins_encode %{ |
| __ fmv_w_x(as_FloatRegister($dst$$reg), zr); |
| %} |
| |
| ins_pipe(fp_load_constant_s); |
| %} |
| |
| // Load Double Constant |
| instruct loadConD(fRegD dst, immD con) %{ |
| match(Set dst con); |
| |
| ins_cost(LOAD_COST); |
| format %{ |
| "fld $dst, [$constantaddress]\t# load from constant table: double=$con, #@loadConD" |
| %} |
| |
| ins_encode %{ |
| __ fld(as_FloatRegister($dst$$reg), $constantaddress($con)); |
| %} |
| |
| ins_pipe(fp_load_constant_d); |
| %} |
| |
| instruct loadConD0(fRegD dst, immD0 con) %{ |
| match(Set dst con); |
| |
| ins_cost(XFER_COST); |
| |
| format %{ "fmv.d.x $dst, zr\t# double, #@loadConD0" %} |
| |
| ins_encode %{ |
| __ fmv_d_x(as_FloatRegister($dst$$reg), zr); |
| %} |
| |
| ins_pipe(fp_load_constant_d); |
| %} |
| |
| // Store Instructions |
| // Store CMS card-mark Immediate |
| instruct storeimmCM0(immI0 zero, memory mem) |
| %{ |
| match(Set mem (StoreCM mem zero)); |
| |
| ins_cost(STORE_COST); |
| format %{ "storestore (elided)\n\t" |
| "sb zr, $mem\t# byte, #@storeimmCM0" %} |
| |
| ins_encode %{ |
| __ sb(zr, Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_mem); |
| %} |
| |
| // Store CMS card-mark Immediate with intervening StoreStore |
| // needed when using CMS with no conditional card marking |
| instruct storeimmCM0_ordered(immI0 zero, memory mem) |
| %{ |
| match(Set mem (StoreCM mem zero)); |
| |
| ins_cost(ALU_COST + STORE_COST); |
| format %{ "membar(StoreStore)\n\t" |
| "sb zr, $mem\t# byte, #@storeimmCM0_ordered" %} |
| |
| ins_encode %{ |
| __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); |
| __ sb(zr, Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_mem); |
| %} |
| |
| // Store Byte |
| instruct storeB(iRegIorL2I src, memory mem) |
| %{ |
| match(Set mem (StoreB mem src)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sb $src, $mem\t# byte, #@storeB" %} |
| |
| ins_encode %{ |
| __ sb(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_mem); |
| %} |
| |
| instruct storeimmB0(immI0 zero, memory mem) |
| %{ |
| match(Set mem (StoreB mem zero)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sb zr, $mem\t# byte, #@storeimmB0" %} |
| |
| ins_encode %{ |
| __ sb(zr, Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_mem); |
| %} |
| |
| // Store Char/Short |
| instruct storeC(iRegIorL2I src, memory mem) |
| %{ |
| match(Set mem (StoreC mem src)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sh $src, $mem\t# short, #@storeC" %} |
| |
| ins_encode %{ |
| __ sh(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_mem); |
| %} |
| |
| instruct storeimmC0(immI0 zero, memory mem) |
| %{ |
| match(Set mem (StoreC mem zero)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sh zr, $mem\t# short, #@storeimmC0" %} |
| |
| ins_encode %{ |
| __ sh(zr, Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_mem); |
| %} |
| |
| // Store Integer |
| instruct storeI(iRegIorL2I src, memory mem) |
| %{ |
| match(Set mem(StoreI mem src)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sw $src, $mem\t# int, #@storeI" %} |
| |
| ins_encode %{ |
| __ sw(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_mem); |
| %} |
| |
| instruct storeimmI0(immI0 zero, memory mem) |
| %{ |
| match(Set mem(StoreI mem zero)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sw zr, $mem\t# int, #@storeimmI0" %} |
| |
| ins_encode %{ |
| __ sw(zr, Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_mem); |
| %} |
| |
| // Store Long (64 bit signed) |
| instruct storeL(iRegL src, memory mem) |
| %{ |
| match(Set mem (StoreL mem src)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sd $src, $mem\t# long, #@storeL" %} |
| |
| ins_encode %{ |
| __ sd(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_mem); |
| %} |
| |
| // Store Long (64 bit signed) |
| instruct storeimmL0(immL0 zero, memory mem) |
| %{ |
| match(Set mem (StoreL mem zero)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sd zr, $mem\t# long, #@storeimmL0" %} |
| |
| ins_encode %{ |
| __ sd(zr, Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_mem); |
| %} |
| |
| // Store Pointer |
| instruct storeP(iRegP src, memory mem) |
| %{ |
| match(Set mem (StoreP mem src)); |
| predicate(n->as_Store()->barrier_data() == 0); |
| |
| ins_cost(STORE_COST); |
| format %{ "sd $src, $mem\t# ptr, #@storeP" %} |
| |
| ins_encode %{ |
| __ sd(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_mem); |
| %} |
| |
| // Store Pointer |
| instruct storeimmP0(immP0 zero, memory mem) |
| %{ |
| match(Set mem (StoreP mem zero)); |
| predicate(n->as_Store()->barrier_data() == 0); |
| |
| ins_cost(STORE_COST); |
| format %{ "sd zr, $mem\t# ptr, #@storeimmP0" %} |
| |
| ins_encode %{ |
| __ sd(zr, Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_mem); |
| %} |
| |
| // Store Compressed Pointer |
| instruct storeN(iRegN src, memory mem) |
| %{ |
| match(Set mem (StoreN mem src)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sw $src, $mem\t# compressed ptr, #@storeN" %} |
| |
| ins_encode %{ |
| __ sw(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_mem); |
| %} |
| |
| instruct storeImmN0(immN0 zero, memory mem) |
| %{ |
| match(Set mem (StoreN mem zero)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sw zr, $mem\t# compressed ptr, #@storeImmN0" %} |
| |
| ins_encode %{ |
| __ sw(zr, Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_mem); |
| %} |
| |
| // Store Float |
| instruct storeF(fRegF src, memory mem) |
| %{ |
| match(Set mem (StoreF mem src)); |
| |
| ins_cost(STORE_COST); |
| format %{ "fsw $src, $mem\t# float, #@storeF" %} |
| |
| ins_encode %{ |
| __ fsw(as_FloatRegister($src$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(fp_store_reg_s); |
| %} |
| |
| // Store Double |
| instruct storeD(fRegD src, memory mem) |
| %{ |
| match(Set mem (StoreD mem src)); |
| |
| ins_cost(STORE_COST); |
| format %{ "fsd $src, $mem\t# double, #@storeD" %} |
| |
| ins_encode %{ |
| __ fsd(as_FloatRegister($src$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(fp_store_reg_d); |
| %} |
| |
| // Store Compressed Klass Pointer |
| instruct storeNKlass(iRegN src, memory mem) |
| %{ |
| match(Set mem (StoreNKlass mem src)); |
| |
| ins_cost(STORE_COST); |
| format %{ "sw $src, $mem\t# compressed klass ptr, #@storeNKlass" %} |
| |
| ins_encode %{ |
| __ sw(as_Register($src$$reg), Address(as_Register($mem$$base), $mem$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_mem); |
| %} |
| |
| // ============================================================================ |
| // Prefetch instructions |
| // Must be safe to execute with invalid address (cannot fault). |
| |
| instruct prefetchalloc( memory mem ) %{ |
| predicate(UseZicbop); |
| match(PrefetchAllocation mem); |
| |
| ins_cost(ALU_COST * 1); |
| format %{ "prefetch_w $mem\t# Prefetch for write" %} |
| |
| ins_encode %{ |
| if (Assembler::is_simm12($mem$$disp)) { |
| if (($mem$$disp & 0x1f) == 0) { |
| __ prefetch_w(as_Register($mem$$base), $mem$$disp); |
| } else { |
| __ addi(t0, as_Register($mem$$base), $mem$$disp); |
| __ prefetch_w(t0, 0); |
| } |
| } else { |
| __ mv(t0, $mem$$disp); |
| __ add(t0, as_Register($mem$$base), t0); |
| __ prefetch_w(t0, 0); |
| } |
| %} |
| |
| ins_pipe(iload_prefetch); |
| %} |
| |
| // ============================================================================ |
| // Atomic operation instructions |
| // |
| |
| // standard CompareAndSwapX when we are using barriers |
| // these have higher priority than the rules selected by a predicate |
| instruct compareAndSwapB(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| match(Set res (CompareAndSwapB mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 10 + BRANCH_COST * 4); |
| |
| effect(TEMP_DEF res, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); |
| |
| format %{ |
| "cmpxchg $mem, $oldval, $newval\t# (byte) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapB" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8, |
| Assembler::relaxed /* acquire */, Assembler::rl /* release */, $res$$Register, |
| true /* result as bool */, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapS(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| match(Set res (CompareAndSwapS mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 11 + BRANCH_COST * 4); |
| |
| effect(TEMP_DEF res, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3, KILL cr); |
| |
| format %{ |
| "cmpxchg $mem, $oldval, $newval\t# (short) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapS" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16, |
| Assembler::relaxed /* acquire */, Assembler::rl /* release */, $res$$Register, |
| true /* result as bool */, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval) |
| %{ |
| match(Set res (CompareAndSwapI mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4); |
| |
| format %{ |
| "cmpxchg $mem, $oldval, $newval\t# (int) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapI" |
| %} |
| |
| ins_encode(riscv_enc_cmpxchgw(res, mem, oldval, newval)); |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval) |
| %{ |
| match(Set res (CompareAndSwapL mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4); |
| |
| format %{ |
| "cmpxchg $mem, $oldval, $newval\t# (long) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapL" |
| %} |
| |
| ins_encode(riscv_enc_cmpxchg(res, mem, oldval, newval)); |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval) |
| %{ |
| predicate(n->as_LoadStore()->barrier_data() == 0); |
| |
| match(Set res (CompareAndSwapP mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4); |
| |
| format %{ |
| "cmpxchg $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapP" |
| %} |
| |
| ins_encode(riscv_enc_cmpxchg(res, mem, oldval, newval)); |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval) |
| %{ |
| match(Set res (CompareAndSwapN mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 8 + BRANCH_COST * 4); |
| |
| format %{ |
| "cmpxchg $mem, $oldval, $newval\t# (narrow oop) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapN" |
| %} |
| |
| ins_encode(riscv_enc_cmpxchgn(res, mem, oldval, newval)); |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| // alternative CompareAndSwapX when we are eliding barriers |
| instruct compareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndSwapB mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 10 + BRANCH_COST * 4); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg_acq $mem, $oldval, $newval\t# (byte) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapBAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8, |
| Assembler::aq /* acquire */, Assembler::rl /* release */, $res$$Register, |
| true /* result as bool */, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndSwapS mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 11 + BRANCH_COST * 4); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg_acq $mem, $oldval, $newval\t# (short) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapSAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16, |
| Assembler::aq /* acquire */, Assembler::rl /* release */, $res$$Register, |
| true /* result as bool */, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndSwapI mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4); |
| |
| format %{ |
| "cmpxchg_acq $mem, $oldval, $newval\t# (int) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapIAcq" |
| %} |
| |
| ins_encode(riscv_enc_cmpxchgw_acq(res, mem, oldval, newval)); |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndSwapL mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4); |
| |
| format %{ |
| "cmpxchg_acq $mem, $oldval, $newval\t# (long) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapLAcq" |
| %} |
| |
| ins_encode(riscv_enc_cmpxchg_acq(res, mem, oldval, newval)); |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == 0)); |
| |
| match(Set res (CompareAndSwapP mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 6 + BRANCH_COST * 4); |
| |
| format %{ |
| "cmpxchg_acq $mem, $oldval, $newval\t# (ptr) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapPAcq" |
| %} |
| |
| ins_encode(riscv_enc_cmpxchg_acq(res, mem, oldval, newval)); |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndSwapN mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + ALU_COST * 8 + BRANCH_COST * 4); |
| |
| format %{ |
| "cmpxchg_acq $mem, $oldval, $newval\t# (narrow oop) if $mem == $oldval then $mem <-- $newval\n\t" |
| "mv $res, $res == $oldval\t# $res <-- ($res == $oldval ? 1 : 0), #@compareAndSwapNAcq" |
| %} |
| |
| ins_encode(riscv_enc_cmpxchgn_acq(res, mem, oldval, newval)); |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| // Sundry CAS operations. Note that release is always true, |
| // regardless of the memory ordering of the CAS. This is because we |
| // need the volatile case to be sequentially consistent but there is |
| // no trailing StoreLoad barrier emitted by C2. Unfortunately we |
| // can't check the type of memory ordering here, so we always emit a |
| // sc_d(w) with rl bit set. |
| instruct compareAndExchangeB(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| match(Set res (CompareAndExchangeB mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 5); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg $res = $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeB" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register, |
| /*result_as_bool*/ false, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeS(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| match(Set res (CompareAndExchangeS mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 6); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg $res = $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeS" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register, |
| /*result_as_bool*/ false, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval) |
| %{ |
| match(Set res (CompareAndExchangeI mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST); |
| |
| effect(TEMP_DEF res); |
| |
| format %{ |
| "cmpxchg $res = $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeI" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeL(iRegLNoSp res, indirect mem, iRegL oldval, iRegL newval) |
| %{ |
| match(Set res (CompareAndExchangeL mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST); |
| |
| effect(TEMP_DEF res); |
| |
| format %{ |
| "cmpxchg $res = $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeL" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeN(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval) |
| %{ |
| match(Set res (CompareAndExchangeN mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 3); |
| |
| effect(TEMP_DEF res); |
| |
| format %{ |
| "cmpxchg $res = $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeN" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeP(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval) |
| %{ |
| predicate(n->as_LoadStore()->barrier_data() == 0); |
| match(Set res (CompareAndExchangeP mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST); |
| |
| effect(TEMP_DEF res); |
| |
| format %{ |
| "cmpxchg $res = $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeP" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeBAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndExchangeB mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 5); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg_acq $res = $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeBAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register, |
| /*result_as_bool*/ false, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeSAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndExchangeS mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST * 6); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg_acq $res = $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeSAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register, |
| /*result_as_bool*/ false, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndExchangeI mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST); |
| |
| effect(TEMP_DEF res); |
| |
| format %{ |
| "cmpxchg_acq $res = $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeIAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeLAcq(iRegLNoSp res, indirect mem, iRegL oldval, iRegL newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndExchangeL mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST); |
| |
| effect(TEMP_DEF res); |
| |
| format %{ |
| "cmpxchg_acq $res = $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeLAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangeNAcq(iRegNNoSp res, indirect mem, iRegN oldval, iRegN newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (CompareAndExchangeN mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST); |
| |
| effect(TEMP_DEF res); |
| |
| format %{ |
| "cmpxchg_acq $res = $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangeNAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct compareAndExchangePAcq(iRegPNoSp res, indirect mem, iRegP oldval, iRegP newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == 0)); |
| |
| match(Set res (CompareAndExchangeP mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 3 + ALU_COST); |
| |
| effect(TEMP_DEF res); |
| |
| format %{ |
| "cmpxchg_acq $res = $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval, #@compareAndExchangePAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapB(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| match(Set res (WeakCompareAndSwapB mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 6); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg_weak $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapB" |
| %} |
| |
| ins_encode %{ |
| __ weak_cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapS(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| match(Set res (WeakCompareAndSwapS mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 7); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg_weak $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapS" |
| %} |
| |
| ins_encode %{ |
| __ weak_cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapI(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval) |
| %{ |
| match(Set res (WeakCompareAndSwapI mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); |
| |
| format %{ |
| "cmpxchg_weak $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapI" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapL(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval) |
| %{ |
| match(Set res (WeakCompareAndSwapL mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); |
| |
| format %{ |
| "cmpxchg_weak $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapL" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapN(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval) |
| %{ |
| match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 4); |
| |
| format %{ |
| "cmpxchg_weak $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapN" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapP(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval) |
| %{ |
| predicate(n->as_LoadStore()->barrier_data() == 0); |
| match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); |
| |
| format %{ |
| "cmpxchg_weak $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapP" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::relaxed, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapBAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (WeakCompareAndSwapB mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 6); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg_weak_acq $mem, $oldval, $newval\t# (byte, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapBAcq" |
| %} |
| |
| ins_encode %{ |
| __ weak_cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int8, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapSAcq(iRegINoSp res, indirect mem, iRegI_R12 oldval, iRegI_R13 newval, |
| iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, rFlagsReg cr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (WeakCompareAndSwapS mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 7); |
| |
| effect(TEMP_DEF res, KILL cr, USE_KILL oldval, USE_KILL newval, TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| format %{ |
| "cmpxchg_weak_acq $mem, $oldval, $newval\t# (short, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapSAcq" |
| %} |
| |
| ins_encode %{ |
| __ weak_cmpxchg_narrow_value(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int16, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapIAcq(iRegINoSp res, indirect mem, iRegI oldval, iRegI newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (WeakCompareAndSwapI mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); |
| |
| format %{ |
| "cmpxchg_weak_acq $mem, $oldval, $newval\t# (int, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapIAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int32, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapLAcq(iRegINoSp res, indirect mem, iRegL oldval, iRegL newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (WeakCompareAndSwapL mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); |
| |
| format %{ |
| "cmpxchg_weak_acq $mem, $oldval, $newval\t# (long, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapLAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapNAcq(iRegINoSp res, indirect mem, iRegN oldval, iRegN newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set res (WeakCompareAndSwapN mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 4); |
| |
| format %{ |
| "cmpxchg_weak_acq $mem, $oldval, $newval\t# (narrow oop, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "# $res == 1 when success, #@weakCompareAndSwapNAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::uint32, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct weakCompareAndSwapPAcq(iRegINoSp res, indirect mem, iRegP oldval, iRegP newval) |
| %{ |
| predicate(needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == 0)); |
| |
| match(Set res (WeakCompareAndSwapP mem (Binary oldval newval))); |
| |
| ins_cost(LOAD_COST + STORE_COST + BRANCH_COST * 2 + ALU_COST * 2); |
| |
| format %{ |
| "cmpxchg_weak_acq $mem, $oldval, $newval\t# (ptr, weak) if $mem == $oldval then $mem <-- $newval\n\t" |
| "\t# $res == 1 when success, #@weakCompareAndSwapPAcq" |
| %} |
| |
| ins_encode %{ |
| __ cmpxchg_weak(as_Register($mem$$base), $oldval$$Register, $newval$$Register, Assembler::int64, |
| /*acquire*/ Assembler::aq, /*release*/ Assembler::rl, $res$$Register); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct get_and_setI(indirect mem, iRegI newv, iRegINoSp prev) |
| %{ |
| match(Set prev (GetAndSetI mem newv)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "atomic_xchgw $prev, $newv, [$mem]\t#@get_and_setI" %} |
| |
| ins_encode %{ |
| __ atomic_xchgw($prev$$Register, $newv$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_setL(indirect mem, iRegL newv, iRegLNoSp prev) |
| %{ |
| match(Set prev (GetAndSetL mem newv)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "atomic_xchg $prev, $newv, [$mem]\t#@get_and_setL" %} |
| |
| ins_encode %{ |
| __ atomic_xchg($prev$$Register, $newv$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_setN(indirect mem, iRegN newv, iRegINoSp prev) |
| %{ |
| match(Set prev (GetAndSetN mem newv)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "atomic_xchgwu $prev, $newv, [$mem]\t#@get_and_setN" %} |
| |
| ins_encode %{ |
| __ atomic_xchgwu($prev$$Register, $newv$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_setP(indirect mem, iRegP newv, iRegPNoSp prev) |
| %{ |
| predicate(n->as_LoadStore()->barrier_data() == 0); |
| match(Set prev (GetAndSetP mem newv)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "atomic_xchg $prev, $newv, [$mem]\t#@get_and_setP" %} |
| |
| ins_encode %{ |
| __ atomic_xchg($prev$$Register, $newv$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_setIAcq(indirect mem, iRegI newv, iRegINoSp prev) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set prev (GetAndSetI mem newv)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "atomic_xchgw_acq $prev, $newv, [$mem]\t#@get_and_setIAcq" %} |
| |
| ins_encode %{ |
| __ atomic_xchgalw($prev$$Register, $newv$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_setLAcq(indirect mem, iRegL newv, iRegLNoSp prev) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set prev (GetAndSetL mem newv)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "atomic_xchg_acq $prev, $newv, [$mem]\t#@get_and_setLAcq" %} |
| |
| ins_encode %{ |
| __ atomic_xchgal($prev$$Register, $newv$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_setNAcq(indirect mem, iRegN newv, iRegINoSp prev) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set prev (GetAndSetN mem newv)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "atomic_xchgwu_acq $prev, $newv, [$mem]\t#@get_and_setNAcq" %} |
| |
| ins_encode %{ |
| __ atomic_xchgalwu($prev$$Register, $newv$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_setPAcq(indirect mem, iRegP newv, iRegPNoSp prev) |
| %{ |
| predicate(needs_acquiring_load_reserved(n) && (n->as_LoadStore()->barrier_data() == 0)); |
| |
| match(Set prev (GetAndSetP mem newv)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "atomic_xchg_acq $prev, $newv, [$mem]\t#@get_and_setPAcq" %} |
| |
| ins_encode %{ |
| __ atomic_xchgal($prev$$Register, $newv$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addL(indirect mem, iRegLNoSp newval, iRegL incr) |
| %{ |
| match(Set newval (GetAndAddL mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addL $newval, [$mem], $incr\t#@get_and_addL" %} |
| |
| ins_encode %{ |
| __ atomic_add($newval$$Register, $incr$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addL_no_res(indirect mem, Universe dummy, iRegL incr) |
| %{ |
| predicate(n->as_LoadStore()->result_not_used()); |
| |
| match(Set dummy (GetAndAddL mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addL [$mem], $incr\t#@get_and_addL_no_res" %} |
| |
| ins_encode %{ |
| __ atomic_add(noreg, $incr$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addLi(indirect mem, iRegLNoSp newval, immLAdd incr) |
| %{ |
| match(Set newval (GetAndAddL mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addL $newval, [$mem], $incr\t#@get_and_addLi" %} |
| |
| ins_encode %{ |
| __ atomic_add($newval$$Register, $incr$$constant, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addLi_no_res(indirect mem, Universe dummy, immLAdd incr) |
| %{ |
| predicate(n->as_LoadStore()->result_not_used()); |
| |
| match(Set dummy (GetAndAddL mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addL [$mem], $incr\t#@get_and_addLi_no_res" %} |
| |
| ins_encode %{ |
| __ atomic_add(noreg, $incr$$constant, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addI(indirect mem, iRegINoSp newval, iRegIorL2I incr) |
| %{ |
| match(Set newval (GetAndAddI mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addI $newval, [$mem], $incr\t#@get_and_addI" %} |
| |
| ins_encode %{ |
| __ atomic_addw($newval$$Register, $incr$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addI_no_res(indirect mem, Universe dummy, iRegIorL2I incr) |
| %{ |
| predicate(n->as_LoadStore()->result_not_used()); |
| |
| match(Set dummy (GetAndAddI mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addI [$mem], $incr\t#@get_and_addI_no_res" %} |
| |
| ins_encode %{ |
| __ atomic_addw(noreg, $incr$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addIi(indirect mem, iRegINoSp newval, immIAdd incr) |
| %{ |
| match(Set newval (GetAndAddI mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addI $newval, [$mem], $incr\t#@get_and_addIi" %} |
| |
| ins_encode %{ |
| __ atomic_addw($newval$$Register, $incr$$constant, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addIi_no_res(indirect mem, Universe dummy, immIAdd incr) |
| %{ |
| predicate(n->as_LoadStore()->result_not_used()); |
| |
| match(Set dummy (GetAndAddI mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addI [$mem], $incr\t#@get_and_addIi_no_res" %} |
| |
| ins_encode %{ |
| __ atomic_addw(noreg, $incr$$constant, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addLAcq(indirect mem, iRegLNoSp newval, iRegL incr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set newval (GetAndAddL mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addL_acq $newval, [$mem], $incr\t#@get_and_addLAcq" %} |
| |
| ins_encode %{ |
| __ atomic_addal($newval$$Register, $incr$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addL_no_resAcq(indirect mem, Universe dummy, iRegL incr) %{ |
| predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n)); |
| |
| match(Set dummy (GetAndAddL mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addL_acq [$mem], $incr\t#@get_and_addL_no_resAcq" %} |
| |
| ins_encode %{ |
| __ atomic_addal(noreg, $incr$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addLiAcq(indirect mem, iRegLNoSp newval, immLAdd incr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set newval (GetAndAddL mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addL_acq $newval, [$mem], $incr\t#@get_and_addLiAcq" %} |
| |
| ins_encode %{ |
| __ atomic_addal($newval$$Register, $incr$$constant, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addLi_no_resAcq(indirect mem, Universe dummy, immLAdd incr) |
| %{ |
| predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n)); |
| |
| match(Set dummy (GetAndAddL mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addL_acq [$mem], $incr\t#@get_and_addLi_no_resAcq" %} |
| |
| ins_encode %{ |
| __ atomic_addal(noreg, $incr$$constant, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addIAcq(indirect mem, iRegINoSp newval, iRegIorL2I incr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set newval (GetAndAddI mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addI_acq $newval, [$mem], $incr\t#@get_and_addIAcq" %} |
| |
| ins_encode %{ |
| __ atomic_addalw($newval$$Register, $incr$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addI_no_resAcq(indirect mem, Universe dummy, iRegIorL2I incr) |
| %{ |
| predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n)); |
| |
| match(Set dummy (GetAndAddI mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addI_acq [$mem], $incr\t#@get_and_addI_no_resAcq" %} |
| |
| ins_encode %{ |
| __ atomic_addalw(noreg, $incr$$Register, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addIiAcq(indirect mem, iRegINoSp newval, immIAdd incr) |
| %{ |
| predicate(needs_acquiring_load_reserved(n)); |
| |
| match(Set newval (GetAndAddI mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addI_acq $newval, [$mem], $incr\t#@get_and_addIiAcq" %} |
| |
| ins_encode %{ |
| __ atomic_addalw($newval$$Register, $incr$$constant, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct get_and_addIi_no_resAcq(indirect mem, Universe dummy, immIAdd incr) |
| %{ |
| predicate(n->as_LoadStore()->result_not_used() && needs_acquiring_load_reserved(n)); |
| |
| match(Set dummy (GetAndAddI mem incr)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "get_and_addI_acq [$mem], $incr\t#@get_and_addIi_no_resAcq" %} |
| |
| ins_encode %{ |
| __ atomic_addalw(noreg, $incr$$constant, as_Register($mem$$base)); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| // ============================================================================ |
| // Arithmetic Instructions |
| // |
| |
| // Integer Addition |
| |
| // TODO |
| // these currently employ operations which do not set CR and hence are |
| // not flagged as killing CR but we would like to isolate the cases |
| // where we want to set flags from those where we don't. need to work |
| // out how to do that. |
| instruct addI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ |
| match(Set dst (AddI src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "addw $dst, $src1, $src2\t#@addI_reg_reg" %} |
| |
| ins_encode %{ |
| __ addw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| instruct addI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immIAdd src2) %{ |
| match(Set dst (AddI src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "addiw $dst, $src1, $src2\t#@addI_reg_imm" %} |
| |
| ins_encode %{ |
| int32_t con = (int32_t)$src2$$constant; |
| __ addiw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| $src2$$constant); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| instruct addI_reg_imm_l2i(iRegINoSp dst, iRegL src1, immIAdd src2) %{ |
| match(Set dst (AddI (ConvL2I src1) src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "addiw $dst, $src1, $src2\t#@addI_reg_imm_l2i" %} |
| |
| ins_encode %{ |
| __ addiw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| $src2$$constant); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Pointer Addition |
| instruct addP_reg_reg(iRegPNoSp dst, iRegP src1, iRegL src2) %{ |
| match(Set dst (AddP src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "add $dst, $src1, $src2\t# ptr, #@addP_reg_reg" %} |
| |
| ins_encode %{ |
| __ add(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // If we shift more than 32 bits, we need not convert I2L. |
| instruct lShiftL_regI_immGE32(iRegLNoSp dst, iRegI src, uimmI6_ge32 scale) %{ |
| match(Set dst (LShiftL (ConvI2L src) scale)); |
| ins_cost(ALU_COST); |
| format %{ "slli $dst, $src, $scale & 63\t#@lShiftL_regI_immGE32" %} |
| |
| ins_encode %{ |
| __ slli(as_Register($dst$$reg), as_Register($src$$reg), $scale$$constant & 63); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // Pointer Immediate Addition |
| // n.b. this needs to be more expensive than using an indirect memory |
| // operand |
| instruct addP_reg_imm(iRegPNoSp dst, iRegP src1, immLAdd src2) %{ |
| match(Set dst (AddP src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "addi $dst, $src1, $src2\t# ptr, #@addP_reg_imm" %} |
| |
| ins_encode %{ |
| // src2 is imm, so actually call the addi |
| __ add(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| $src2$$constant); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Long Addition |
| instruct addL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ |
| match(Set dst (AddL src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "add $dst, $src1, $src2\t#@addL_reg_reg" %} |
| |
| ins_encode %{ |
| __ add(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // No constant pool entries requiredLong Immediate Addition. |
| instruct addL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{ |
| match(Set dst (AddL src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "addi $dst, $src1, $src2\t#@addL_reg_imm" %} |
| |
| ins_encode %{ |
| // src2 is imm, so actually call the addi |
| __ add(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| $src2$$constant); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Integer Subtraction |
| instruct subI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ |
| match(Set dst (SubI src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "subw $dst, $src1, $src2\t#@subI_reg_reg" %} |
| |
| ins_encode %{ |
| __ subw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // Immediate Subtraction |
| instruct subI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immISub src2) %{ |
| match(Set dst (SubI src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "addiw $dst, $src1, -$src2\t#@subI_reg_imm" %} |
| |
| ins_encode %{ |
| // src2 is imm, so actually call the addiw |
| __ subw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| $src2$$constant); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Long Subtraction |
| instruct subL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ |
| match(Set dst (SubL src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "sub $dst, $src1, $src2\t#@subL_reg_reg" %} |
| |
| ins_encode %{ |
| __ sub(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // No constant pool entries requiredLong Immediate Subtraction. |
| instruct subL_reg_imm(iRegLNoSp dst, iRegL src1, immLSub src2) %{ |
| match(Set dst (SubL src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "addi $dst, $src1, -$src2\t#@subL_reg_imm" %} |
| |
| ins_encode %{ |
| // src2 is imm, so actually call the addi |
| __ sub(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| $src2$$constant); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Integer Negation (special case for sub) |
| |
| instruct negI_reg(iRegINoSp dst, iRegIorL2I src, immI0 zero) %{ |
| match(Set dst (SubI zero src)); |
| ins_cost(ALU_COST); |
| format %{ "subw $dst, x0, $src\t# int, #@negI_reg" %} |
| |
| ins_encode %{ |
| // actually call the subw |
| __ negw(as_Register($dst$$reg), |
| as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg); |
| %} |
| |
| // Long Negation |
| |
| instruct negL_reg(iRegLNoSp dst, iRegL src, immL0 zero) %{ |
| match(Set dst (SubL zero src)); |
| ins_cost(ALU_COST); |
| format %{ "sub $dst, x0, $src\t# long, #@negL_reg" %} |
| |
| ins_encode %{ |
| // actually call the sub |
| __ neg(as_Register($dst$$reg), |
| as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg); |
| %} |
| |
| // Integer Multiply |
| |
| instruct mulI(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ |
| match(Set dst (MulI src1 src2)); |
| ins_cost(IMUL_COST); |
| format %{ "mulw $dst, $src1, $src2\t#@mulI" %} |
| |
| //this means 2 word multi, and no sign extend to 64 bits |
| ins_encode %{ |
| // riscv64 mulw will sign-extension to high 32 bits in dst reg |
| __ mulw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(imul_reg_reg); |
| %} |
| |
| // Long Multiply |
| |
| instruct mulL(iRegLNoSp dst, iRegL src1, iRegL src2) %{ |
| match(Set dst (MulL src1 src2)); |
| ins_cost(IMUL_COST); |
| format %{ "mul $dst, $src1, $src2\t#@mulL" %} |
| |
| ins_encode %{ |
| __ mul(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(lmul_reg_reg); |
| %} |
| |
| instruct mulHiL_rReg(iRegLNoSp dst, iRegL src1, iRegL src2) |
| %{ |
| match(Set dst (MulHiL src1 src2)); |
| ins_cost(IMUL_COST); |
| format %{ "mulh $dst, $src1, $src2\t# mulhi, #@mulHiL_rReg" %} |
| |
| ins_encode %{ |
| __ mulh(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(lmul_reg_reg); |
| %} |
| |
| instruct umulHiL_rReg(iRegLNoSp dst, iRegL src1, iRegL src2) |
| %{ |
| match(Set dst (UMulHiL src1 src2)); |
| ins_cost(IMUL_COST); |
| format %{ "mulhu $dst, $src1, $src2\t# umulhi, #@umulHiL_rReg" %} |
| |
| ins_encode %{ |
| __ mulhu(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(lmul_reg_reg); |
| %} |
| |
| // Integer Divide |
| |
| instruct divI(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ |
| match(Set dst (DivI src1 src2)); |
| ins_cost(IDIVSI_COST); |
| format %{ "divw $dst, $src1, $src2\t#@divI"%} |
| |
| ins_encode(riscv_enc_divw(dst, src1, src2)); |
| ins_pipe(idiv_reg_reg); |
| %} |
| |
| instruct signExtract(iRegINoSp dst, iRegIorL2I src1, immI_31 div1, immI_31 div2) %{ |
| match(Set dst (URShiftI (RShiftI src1 div1) div2)); |
| ins_cost(ALU_COST); |
| format %{ "srliw $dst, $src1, $div1\t# int signExtract, #@signExtract" %} |
| |
| ins_encode %{ |
| __ srliw(as_Register($dst$$reg), as_Register($src1$$reg), 31); |
| %} |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // Long Divide |
| |
| instruct divL(iRegLNoSp dst, iRegL src1, iRegL src2) %{ |
| match(Set dst (DivL src1 src2)); |
| ins_cost(IDIVDI_COST); |
| format %{ "div $dst, $src1, $src2\t#@divL" %} |
| |
| ins_encode(riscv_enc_div(dst, src1, src2)); |
| ins_pipe(ldiv_reg_reg); |
| %} |
| |
| instruct signExtractL(iRegLNoSp dst, iRegL src1, immI_63 div1, immI_63 div2) %{ |
| match(Set dst (URShiftL (RShiftL src1 div1) div2)); |
| ins_cost(ALU_COST); |
| format %{ "srli $dst, $src1, $div1\t# long signExtract, #@signExtractL" %} |
| |
| ins_encode %{ |
| __ srli(as_Register($dst$$reg), as_Register($src1$$reg), 63); |
| %} |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // Integer Remainder |
| |
| instruct modI(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ |
| match(Set dst (ModI src1 src2)); |
| ins_cost(IDIVSI_COST); |
| format %{ "remw $dst, $src1, $src2\t#@modI" %} |
| |
| ins_encode(riscv_enc_modw(dst, src1, src2)); |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // Long Remainder |
| |
| instruct modL(iRegLNoSp dst, iRegL src1, iRegL src2) %{ |
| match(Set dst (ModL src1 src2)); |
| ins_cost(IDIVDI_COST); |
| format %{ "rem $dst, $src1, $src2\t#@modL" %} |
| |
| ins_encode(riscv_enc_mod(dst, src1, src2)); |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // Integer Shifts |
| |
| // Shift Left Register |
| // In RV64I, only the low 5 bits of src2 are considered for the shift amount |
| instruct lShiftI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ |
| match(Set dst (LShiftI src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "sllw $dst, $src1, $src2\t#@lShiftI_reg_reg" %} |
| |
| ins_encode %{ |
| __ sllw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg_vshift); |
| %} |
| |
| // Shift Left Immediate |
| instruct lShiftI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immI src2) %{ |
| match(Set dst (LShiftI src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "slliw $dst, $src1, ($src2 & 0x1f)\t#@lShiftI_reg_imm" %} |
| |
| ins_encode %{ |
| // the shift amount is encoded in the lower |
| // 5 bits of the I-immediate field for RV32I |
| __ slliw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (unsigned) $src2$$constant & 0x1f); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // Shift Right Logical Register |
| // In RV64I, only the low 5 bits of src2 are considered for the shift amount |
| instruct urShiftI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ |
| match(Set dst (URShiftI src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "srlw $dst, $src1, $src2\t#@urShiftI_reg_reg" %} |
| |
| ins_encode %{ |
| __ srlw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg_vshift); |
| %} |
| |
| // Shift Right Logical Immediate |
| instruct urShiftI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immI src2) %{ |
| match(Set dst (URShiftI src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "srliw $dst, $src1, ($src2 & 0x1f)\t#@urShiftI_reg_imm" %} |
| |
| ins_encode %{ |
| // the shift amount is encoded in the lower |
| // 6 bits of the I-immediate field for RV64I |
| __ srliw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (unsigned) $src2$$constant & 0x1f); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // Shift Right Arithmetic Register |
| // In RV64I, only the low 5 bits of src2 are considered for the shift amount |
| instruct rShiftI_reg_reg(iRegINoSp dst, iRegIorL2I src1, iRegIorL2I src2) %{ |
| match(Set dst (RShiftI src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "sraw $dst, $src1, $src2\t#@rShiftI_reg_reg" %} |
| |
| ins_encode %{ |
| // riscv will sign-ext dst high 32 bits |
| __ sraw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg_vshift); |
| %} |
| |
| // Shift Right Arithmetic Immediate |
| instruct rShiftI_reg_imm(iRegINoSp dst, iRegIorL2I src1, immI src2) %{ |
| match(Set dst (RShiftI src1 src2)); |
| ins_cost(ALU_COST); |
| format %{ "sraiw $dst, $src1, ($src2 & 0x1f)\t#@rShiftI_reg_imm" %} |
| |
| ins_encode %{ |
| // riscv will sign-ext dst high 32 bits |
| __ sraiw(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (unsigned) $src2$$constant & 0x1f); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // Long Shifts |
| |
| // Shift Left Register |
| // In RV64I, only the low 6 bits of src2 are considered for the shift amount |
| instruct lShiftL_reg_reg(iRegLNoSp dst, iRegL src1, iRegIorL2I src2) %{ |
| match(Set dst (LShiftL src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "sll $dst, $src1, $src2\t#@lShiftL_reg_reg" %} |
| |
| ins_encode %{ |
| __ sll(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg_vshift); |
| %} |
| |
| // Shift Left Immediate |
| instruct lShiftL_reg_imm(iRegLNoSp dst, iRegL src1, immI src2) %{ |
| match(Set dst (LShiftL src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "slli $dst, $src1, ($src2 & 0x3f)\t#@lShiftL_reg_imm" %} |
| |
| ins_encode %{ |
| // the shift amount is encoded in the lower |
| // 6 bits of the I-immediate field for RV64I |
| __ slli(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (unsigned) $src2$$constant & 0x3f); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // Shift Right Logical Register |
| // In RV64I, only the low 6 bits of src2 are considered for the shift amount |
| instruct urShiftL_reg_reg(iRegLNoSp dst, iRegL src1, iRegIorL2I src2) %{ |
| match(Set dst (URShiftL src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "srl $dst, $src1, $src2\t#@urShiftL_reg_reg" %} |
| |
| ins_encode %{ |
| __ srl(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg_vshift); |
| %} |
| |
| // Shift Right Logical Immediate |
| instruct urShiftL_reg_imm(iRegLNoSp dst, iRegL src1, immI src2) %{ |
| match(Set dst (URShiftL src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "srli $dst, $src1, ($src2 & 0x3f)\t#@urShiftL_reg_imm" %} |
| |
| ins_encode %{ |
| // the shift amount is encoded in the lower |
| // 6 bits of the I-immediate field for RV64I |
| __ srli(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (unsigned) $src2$$constant & 0x3f); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // A special-case pattern for card table stores. |
| instruct urShiftP_reg_imm(iRegLNoSp dst, iRegP src1, immI src2) %{ |
| match(Set dst (URShiftL (CastP2X src1) src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "srli $dst, p2x($src1), ($src2 & 0x3f)\t#@urShiftP_reg_imm" %} |
| |
| ins_encode %{ |
| // the shift amount is encoded in the lower |
| // 6 bits of the I-immediate field for RV64I |
| __ srli(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (unsigned) $src2$$constant & 0x3f); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // Shift Right Arithmetic Register |
| // In RV64I, only the low 6 bits of src2 are considered for the shift amount |
| instruct rShiftL_reg_reg(iRegLNoSp dst, iRegL src1, iRegIorL2I src2) %{ |
| match(Set dst (RShiftL src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "sra $dst, $src1, $src2\t#@rShiftL_reg_reg" %} |
| |
| ins_encode %{ |
| __ sra(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg_vshift); |
| %} |
| |
| // Shift Right Arithmetic Immediate |
| instruct rShiftL_reg_imm(iRegLNoSp dst, iRegL src1, immI src2) %{ |
| match(Set dst (RShiftL src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "srai $dst, $src1, ($src2 & 0x3f)\t#@rShiftL_reg_imm" %} |
| |
| ins_encode %{ |
| // the shift amount is encoded in the lower |
| // 6 bits of the I-immediate field for RV64I |
| __ srai(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (unsigned) $src2$$constant & 0x3f); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| instruct regI_not_reg(iRegINoSp dst, iRegI src1, immI_M1 m1) %{ |
| match(Set dst (XorI src1 m1)); |
| ins_cost(ALU_COST); |
| format %{ "xori $dst, $src1, -1\t#@regI_not_reg" %} |
| |
| ins_encode %{ |
| __ xori(as_Register($dst$$reg), as_Register($src1$$reg), -1); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| instruct regL_not_reg(iRegLNoSp dst, iRegL src1, immL_M1 m1) %{ |
| match(Set dst (XorL src1 m1)); |
| ins_cost(ALU_COST); |
| format %{ "xori $dst, $src1, -1\t#@regL_not_reg" %} |
| |
| ins_encode %{ |
| __ xori(as_Register($dst$$reg), as_Register($src1$$reg), -1); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| |
| // ============================================================================ |
| // Floating Point Arithmetic Instructions |
| |
| instruct addF_reg_reg(fRegF dst, fRegF src1, fRegF src2) %{ |
| match(Set dst (AddF src1 src2)); |
| |
| ins_cost(DEFAULT_COST * 5); |
| format %{ "fadd.s $dst, $src1, $src2\t#@addF_reg_reg" %} |
| |
| ins_encode %{ |
| __ fadd_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg)); |
| %} |
| |
| ins_pipe(fp_dop_reg_reg_s); |
| %} |
| |
| instruct addD_reg_reg(fRegD dst, fRegD src1, fRegD src2) %{ |
| match(Set dst (AddD src1 src2)); |
| |
| ins_cost(DEFAULT_COST * 5); |
| format %{ "fadd.d $dst, $src1, $src2\t#@addD_reg_reg" %} |
| |
| ins_encode %{ |
| __ fadd_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg)); |
| %} |
| |
| ins_pipe(fp_dop_reg_reg_d); |
| %} |
| |
| instruct subF_reg_reg(fRegF dst, fRegF src1, fRegF src2) %{ |
| match(Set dst (SubF src1 src2)); |
| |
| ins_cost(DEFAULT_COST * 5); |
| format %{ "fsub.s $dst, $src1, $src2\t#@subF_reg_reg" %} |
| |
| ins_encode %{ |
| __ fsub_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg)); |
| %} |
| |
| ins_pipe(fp_dop_reg_reg_s); |
| %} |
| |
| instruct subD_reg_reg(fRegD dst, fRegD src1, fRegD src2) %{ |
| match(Set dst (SubD src1 src2)); |
| |
| ins_cost(DEFAULT_COST * 5); |
| format %{ "fsub.d $dst, $src1, $src2\t#@subD_reg_reg" %} |
| |
| ins_encode %{ |
| __ fsub_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg)); |
| %} |
| |
| ins_pipe(fp_dop_reg_reg_d); |
| %} |
| |
| instruct mulF_reg_reg(fRegF dst, fRegF src1, fRegF src2) %{ |
| match(Set dst (MulF src1 src2)); |
| |
| ins_cost(FMUL_SINGLE_COST); |
| format %{ "fmul.s $dst, $src1, $src2\t#@mulF_reg_reg" %} |
| |
| ins_encode %{ |
| __ fmul_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg)); |
| %} |
| |
| ins_pipe(fp_dop_reg_reg_s); |
| %} |
| |
| instruct mulD_reg_reg(fRegD dst, fRegD src1, fRegD src2) %{ |
| match(Set dst (MulD src1 src2)); |
| |
| ins_cost(FMUL_DOUBLE_COST); |
| format %{ "fmul.d $dst, $src1, $src2\t#@mulD_reg_reg" %} |
| |
| ins_encode %{ |
| __ fmul_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg)); |
| %} |
| |
| ins_pipe(fp_dop_reg_reg_d); |
| %} |
| |
| // src1 * src2 + src3 |
| instruct maddF_reg_reg(fRegF dst, fRegF src1, fRegF src2, fRegF src3) %{ |
| predicate(UseFMA); |
| match(Set dst (FmaF src3 (Binary src1 src2))); |
| |
| ins_cost(FMUL_SINGLE_COST); |
| format %{ "fmadd.s $dst, $src1, $src2, $src3\t#@maddF_reg_reg" %} |
| |
| ins_encode %{ |
| __ fmadd_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg), |
| as_FloatRegister($src3$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // src1 * src2 + src3 |
| instruct maddD_reg_reg(fRegD dst, fRegD src1, fRegD src2, fRegD src3) %{ |
| predicate(UseFMA); |
| match(Set dst (FmaD src3 (Binary src1 src2))); |
| |
| ins_cost(FMUL_DOUBLE_COST); |
| format %{ "fmadd.d $dst, $src1, $src2, $src3\t#@maddD_reg_reg" %} |
| |
| ins_encode %{ |
| __ fmadd_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg), |
| as_FloatRegister($src3$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // src1 * src2 - src3 |
| instruct msubF_reg_reg(fRegF dst, fRegF src1, fRegF src2, fRegF src3) %{ |
| predicate(UseFMA); |
| match(Set dst (FmaF (NegF src3) (Binary src1 src2))); |
| |
| ins_cost(FMUL_SINGLE_COST); |
| format %{ "fmsub.s $dst, $src1, $src2, $src3\t#@msubF_reg_reg" %} |
| |
| ins_encode %{ |
| __ fmsub_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg), |
| as_FloatRegister($src3$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // src1 * src2 - src3 |
| instruct msubD_reg_reg(fRegD dst, fRegD src1, fRegD src2, fRegD src3) %{ |
| predicate(UseFMA); |
| match(Set dst (FmaD (NegD src3) (Binary src1 src2))); |
| |
| ins_cost(FMUL_DOUBLE_COST); |
| format %{ "fmsub.d $dst, $src1, $src2, $src3\t#@msubD_reg_reg" %} |
| |
| ins_encode %{ |
| __ fmsub_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg), |
| as_FloatRegister($src3$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // -src1 * src2 + src3 |
| instruct nmsubF_reg_reg(fRegF dst, fRegF src1, fRegF src2, fRegF src3) %{ |
| predicate(UseFMA); |
| match(Set dst (FmaF src3 (Binary (NegF src1) src2))); |
| match(Set dst (FmaF src3 (Binary src1 (NegF src2)))); |
| |
| ins_cost(FMUL_SINGLE_COST); |
| format %{ "fnmsub.s $dst, $src1, $src2, $src3\t#@nmsubF_reg_reg" %} |
| |
| ins_encode %{ |
| __ fnmsub_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg), |
| as_FloatRegister($src3$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // -src1 * src2 + src3 |
| instruct nmsubD_reg_reg(fRegD dst, fRegD src1, fRegD src2, fRegD src3) %{ |
| predicate(UseFMA); |
| match(Set dst (FmaD src3 (Binary (NegD src1) src2))); |
| match(Set dst (FmaD src3 (Binary src1 (NegD src2)))); |
| |
| ins_cost(FMUL_DOUBLE_COST); |
| format %{ "fnmsub.d $dst, $src1, $src2, $src3\t#@nmsubD_reg_reg" %} |
| |
| ins_encode %{ |
| __ fnmsub_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg), |
| as_FloatRegister($src3$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // -src1 * src2 - src3 |
| instruct nmaddF_reg_reg(fRegF dst, fRegF src1, fRegF src2, fRegF src3) %{ |
| predicate(UseFMA); |
| match(Set dst (FmaF (NegF src3) (Binary (NegF src1) src2))); |
| match(Set dst (FmaF (NegF src3) (Binary src1 (NegF src2)))); |
| |
| ins_cost(FMUL_SINGLE_COST); |
| format %{ "fnmadd.s $dst, $src1, $src2, $src3\t#@nmaddF_reg_reg" %} |
| |
| ins_encode %{ |
| __ fnmadd_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg), |
| as_FloatRegister($src3$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // -src1 * src2 - src3 |
| instruct nmaddD_reg_reg(fRegD dst, fRegD src1, fRegD src2, fRegD src3) %{ |
| predicate(UseFMA); |
| match(Set dst (FmaD (NegD src3) (Binary (NegD src1) src2))); |
| match(Set dst (FmaD (NegD src3) (Binary src1 (NegD src2)))); |
| |
| ins_cost(FMUL_DOUBLE_COST); |
| format %{ "fnmadd.d $dst, $src1, $src2, $src3\t#@nmaddD_reg_reg" %} |
| |
| ins_encode %{ |
| __ fnmadd_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg), |
| as_FloatRegister($src3$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // Math.max(FF)F |
| instruct maxF_reg_reg(fRegF dst, fRegF src1, fRegF src2, rFlagsReg cr) %{ |
| match(Set dst (MaxF src1 src2)); |
| effect(TEMP_DEF dst, KILL cr); |
| |
| format %{ "maxF $dst, $src1, $src2" %} |
| |
| ins_encode %{ |
| __ minmax_fp(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg), |
| false /* is_double */, false /* is_min */); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // Math.min(FF)F |
| instruct minF_reg_reg(fRegF dst, fRegF src1, fRegF src2, rFlagsReg cr) %{ |
| match(Set dst (MinF src1 src2)); |
| effect(TEMP_DEF dst, KILL cr); |
| |
| format %{ "minF $dst, $src1, $src2" %} |
| |
| ins_encode %{ |
| __ minmax_fp(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg), |
| false /* is_double */, true /* is_min */); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // Math.max(DD)D |
| instruct maxD_reg_reg(fRegD dst, fRegD src1, fRegD src2, rFlagsReg cr) %{ |
| match(Set dst (MaxD src1 src2)); |
| effect(TEMP_DEF dst, KILL cr); |
| |
| format %{ "maxD $dst, $src1, $src2" %} |
| |
| ins_encode %{ |
| __ minmax_fp(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg), |
| true /* is_double */, false /* is_min */); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // Math.min(DD)D |
| instruct minD_reg_reg(fRegD dst, fRegD src1, fRegD src2, rFlagsReg cr) %{ |
| match(Set dst (MinD src1 src2)); |
| effect(TEMP_DEF dst, KILL cr); |
| |
| format %{ "minD $dst, $src1, $src2" %} |
| |
| ins_encode %{ |
| __ minmax_fp(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), as_FloatRegister($src2$$reg), |
| true /* is_double */, true /* is_min */); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // Float.isInfinite |
| instruct isInfiniteF_reg_reg(iRegINoSp dst, fRegF src) |
| %{ |
| match(Set dst (IsInfiniteF src)); |
| |
| format %{ "isInfinite $dst, $src" %} |
| ins_encode %{ |
| __ fclass_s(as_Register($dst$$reg), as_FloatRegister($src$$reg)); |
| __ andi(as_Register($dst$$reg), as_Register($dst$$reg), 0b0010000001); |
| __ slt(as_Register($dst$$reg), zr, as_Register($dst$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // Double.isInfinite |
| instruct isInfiniteD_reg_reg(iRegINoSp dst, fRegD src) |
| %{ |
| match(Set dst (IsInfiniteD src)); |
| |
| format %{ "isInfinite $dst, $src" %} |
| ins_encode %{ |
| __ fclass_d(as_Register($dst$$reg), as_FloatRegister($src$$reg)); |
| __ andi(as_Register($dst$$reg), as_Register($dst$$reg), 0b0010000001); |
| __ slt(as_Register($dst$$reg), zr, as_Register($dst$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // Float.isFinite |
| instruct isFiniteF_reg_reg(iRegINoSp dst, fRegF src) |
| %{ |
| match(Set dst (IsFiniteF src)); |
| |
| format %{ "isFinite $dst, $src" %} |
| ins_encode %{ |
| __ fclass_s(as_Register($dst$$reg), as_FloatRegister($src$$reg)); |
| __ andi(as_Register($dst$$reg), as_Register($dst$$reg), 0b0001111110); |
| __ slt(as_Register($dst$$reg), zr, as_Register($dst$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // Double.isFinite |
| instruct isFiniteD_reg_reg(iRegINoSp dst, fRegD src) |
| %{ |
| match(Set dst (IsFiniteD src)); |
| |
| format %{ "isFinite $dst, $src" %} |
| ins_encode %{ |
| __ fclass_d(as_Register($dst$$reg), as_FloatRegister($src$$reg)); |
| __ andi(as_Register($dst$$reg), as_Register($dst$$reg), 0b0001111110); |
| __ slt(as_Register($dst$$reg), zr, as_Register($dst$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct divF_reg_reg(fRegF dst, fRegF src1, fRegF src2) %{ |
| match(Set dst (DivF src1 src2)); |
| |
| ins_cost(FDIV_COST); |
| format %{ "fdiv.s $dst, $src1, $src2\t#@divF_reg_reg" %} |
| |
| ins_encode %{ |
| __ fdiv_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg)); |
| %} |
| |
| ins_pipe(fp_div_s); |
| %} |
| |
| instruct divD_reg_reg(fRegD dst, fRegD src1, fRegD src2) %{ |
| match(Set dst (DivD src1 src2)); |
| |
| ins_cost(FDIV_COST); |
| format %{ "fdiv.d $dst, $src1, $src2\t#@divD_reg_reg" %} |
| |
| ins_encode %{ |
| __ fdiv_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src1$$reg), |
| as_FloatRegister($src2$$reg)); |
| %} |
| |
| ins_pipe(fp_div_d); |
| %} |
| |
| instruct negF_reg_reg(fRegF dst, fRegF src) %{ |
| match(Set dst (NegF src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fsgnjn.s $dst, $src, $src\t#@negF_reg_reg" %} |
| |
| ins_encode %{ |
| __ fneg_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_uop_s); |
| %} |
| |
| instruct negD_reg_reg(fRegD dst, fRegD src) %{ |
| match(Set dst (NegD src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fsgnjn.d $dst, $src, $src\t#@negD_reg_reg" %} |
| |
| ins_encode %{ |
| __ fneg_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_uop_d); |
| %} |
| |
| instruct absI_reg(iRegINoSp dst, iRegIorL2I src) %{ |
| match(Set dst (AbsI src)); |
| |
| ins_cost(ALU_COST * 3); |
| format %{ |
| "sraiw t0, $src, 0x1f\n\t" |
| "addw $dst, $src, t0\n\t" |
| "xorr $dst, $dst, t0\t#@absI_reg" |
| %} |
| |
| ins_encode %{ |
| __ sraiw(t0, as_Register($src$$reg), 0x1f); |
| __ addw(as_Register($dst$$reg), as_Register($src$$reg), t0); |
| __ xorr(as_Register($dst$$reg), as_Register($dst$$reg), t0); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct absL_reg(iRegLNoSp dst, iRegL src) %{ |
| match(Set dst (AbsL src)); |
| |
| ins_cost(ALU_COST * 3); |
| format %{ |
| "srai t0, $src, 0x3f\n\t" |
| "add $dst, $src, t0\n\t" |
| "xorr $dst, $dst, t0\t#@absL_reg" |
| %} |
| |
| ins_encode %{ |
| __ srai(t0, as_Register($src$$reg), 0x3f); |
| __ add(as_Register($dst$$reg), as_Register($src$$reg), t0); |
| __ xorr(as_Register($dst$$reg), as_Register($dst$$reg), t0); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct absF_reg(fRegF dst, fRegF src) %{ |
| match(Set dst (AbsF src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fsgnjx.s $dst, $src, $src\t#@absF_reg" %} |
| ins_encode %{ |
| __ fabs_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_uop_s); |
| %} |
| |
| instruct absD_reg(fRegD dst, fRegD src) %{ |
| match(Set dst (AbsD src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fsgnjx.d $dst, $src, $src\t#@absD_reg" %} |
| ins_encode %{ |
| __ fabs_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_uop_d); |
| %} |
| |
| instruct sqrtF_reg(fRegF dst, fRegF src) %{ |
| match(Set dst (SqrtF src)); |
| |
| ins_cost(FSQRT_COST); |
| format %{ "fsqrt.s $dst, $src\t#@sqrtF_reg" %} |
| ins_encode %{ |
| __ fsqrt_s(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_sqrt_s); |
| %} |
| |
| instruct sqrtD_reg(fRegD dst, fRegD src) %{ |
| match(Set dst (SqrtD src)); |
| |
| ins_cost(FSQRT_COST); |
| format %{ "fsqrt.d $dst, $src\t#@sqrtD_reg" %} |
| ins_encode %{ |
| __ fsqrt_d(as_FloatRegister($dst$$reg), |
| as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_sqrt_d); |
| %} |
| |
| // Arithmetic Instructions End |
| |
| // ============================================================================ |
| // Logical Instructions |
| |
| // Register And |
| instruct andI_reg_reg(iRegINoSp dst, iRegI src1, iRegI src2) %{ |
| match(Set dst (AndI src1 src2)); |
| |
| format %{ "andr $dst, $src1, $src2\t#@andI_reg_reg" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ andr(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // Immediate And |
| instruct andI_reg_imm(iRegINoSp dst, iRegI src1, immIAdd src2) %{ |
| match(Set dst (AndI src1 src2)); |
| |
| format %{ "andi $dst, $src1, $src2\t#@andI_reg_imm" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ andi(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (int32_t)($src2$$constant)); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Register Or |
| instruct orI_reg_reg(iRegINoSp dst, iRegI src1, iRegI src2) %{ |
| match(Set dst (OrI src1 src2)); |
| |
| format %{ "orr $dst, $src1, $src2\t#@orI_reg_reg" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ orr(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // Immediate Or |
| instruct orI_reg_imm(iRegINoSp dst, iRegI src1, immIAdd src2) %{ |
| match(Set dst (OrI src1 src2)); |
| |
| format %{ "ori $dst, $src1, $src2\t#@orI_reg_imm" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ ori(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (int32_t)($src2$$constant)); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Register Xor |
| instruct xorI_reg_reg(iRegINoSp dst, iRegI src1, iRegI src2) %{ |
| match(Set dst (XorI src1 src2)); |
| |
| format %{ "xorr $dst, $src1, $src2\t#@xorI_reg_reg" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ xorr(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // Immediate Xor |
| instruct xorI_reg_imm(iRegINoSp dst, iRegI src1, immIAdd src2) %{ |
| match(Set dst (XorI src1 src2)); |
| |
| format %{ "xori $dst, $src1, $src2\t#@xorI_reg_imm" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ xori(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (int32_t)($src2$$constant)); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Register And Long |
| instruct andL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ |
| match(Set dst (AndL src1 src2)); |
| |
| format %{ "andr $dst, $src1, $src2\t#@andL_reg_reg" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ andr(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // Immediate And Long |
| instruct andL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{ |
| match(Set dst (AndL src1 src2)); |
| |
| format %{ "andi $dst, $src1, $src2\t#@andL_reg_imm" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ andi(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (int32_t)($src2$$constant)); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Register Or Long |
| instruct orL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ |
| match(Set dst (OrL src1 src2)); |
| |
| format %{ "orr $dst, $src1, $src2\t#@orL_reg_reg" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ orr(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // Immediate Or Long |
| instruct orL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{ |
| match(Set dst (OrL src1 src2)); |
| |
| format %{ "ori $dst, $src1, $src2\t#@orL_reg_imm" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ ori(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (int32_t)($src2$$constant)); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // Register Xor Long |
| instruct xorL_reg_reg(iRegLNoSp dst, iRegL src1, iRegL src2) %{ |
| match(Set dst (XorL src1 src2)); |
| |
| format %{ "xorr $dst, $src1, $src2\t#@xorL_reg_reg" %} |
| |
| ins_cost(ALU_COST); |
| ins_encode %{ |
| __ xorr(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| as_Register($src2$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| // Immediate Xor Long |
| instruct xorL_reg_imm(iRegLNoSp dst, iRegL src1, immLAdd src2) %{ |
| match(Set dst (XorL src1 src2)); |
| |
| ins_cost(ALU_COST); |
| format %{ "xori $dst, $src1, $src2\t#@xorL_reg_imm" %} |
| |
| ins_encode %{ |
| __ xori(as_Register($dst$$reg), |
| as_Register($src1$$reg), |
| (int32_t)($src2$$constant)); |
| %} |
| |
| ins_pipe(ialu_reg_imm); |
| %} |
| |
| // ============================================================================ |
| // BSWAP Instructions |
| |
| instruct bytes_reverse_int(iRegINoSp dst, iRegIorL2I src, rFlagsReg cr) %{ |
| match(Set dst (ReverseBytesI src)); |
| effect(KILL cr); |
| |
| ins_cost(ALU_COST * 13); |
| format %{ "revb_w_w $dst, $src\t#@bytes_reverse_int" %} |
| |
| ins_encode %{ |
| __ revb_w_w(as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct bytes_reverse_long(iRegLNoSp dst, iRegL src, rFlagsReg cr) %{ |
| match(Set dst (ReverseBytesL src)); |
| effect(KILL cr); |
| |
| ins_cost(ALU_COST * 29); |
| format %{ "revb $dst, $src\t#@bytes_reverse_long" %} |
| |
| ins_encode %{ |
| __ revb(as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct bytes_reverse_unsigned_short(iRegINoSp dst, iRegIorL2I src) %{ |
| match(Set dst (ReverseBytesUS src)); |
| |
| ins_cost(ALU_COST * 5); |
| format %{ "revb_h_h_u $dst, $src\t#@bytes_reverse_unsigned_short" %} |
| |
| ins_encode %{ |
| __ revb_h_h_u(as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct bytes_reverse_short(iRegINoSp dst, iRegIorL2I src) %{ |
| match(Set dst (ReverseBytesS src)); |
| |
| ins_cost(ALU_COST * 5); |
| format %{ "revb_h_h $dst, $src\t#@bytes_reverse_short" %} |
| |
| ins_encode %{ |
| __ revb_h_h(as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // ============================================================================ |
| // MemBar Instruction |
| |
| instruct load_fence() %{ |
| match(LoadFence); |
| ins_cost(ALU_COST); |
| |
| format %{ "#@load_fence" %} |
| |
| ins_encode %{ |
| __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); |
| %} |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct membar_acquire() %{ |
| match(MemBarAcquire); |
| ins_cost(ALU_COST); |
| |
| format %{ "#@membar_acquire\n\t" |
| "fence ir iorw" %} |
| |
| ins_encode %{ |
| __ block_comment("membar_acquire"); |
| __ membar(MacroAssembler::LoadLoad | MacroAssembler::LoadStore); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct membar_acquire_lock() %{ |
| match(MemBarAcquireLock); |
| ins_cost(0); |
| |
| format %{ "#@membar_acquire_lock (elided)" %} |
| |
| ins_encode %{ |
| __ block_comment("membar_acquire_lock (elided)"); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct store_fence() %{ |
| match(StoreFence); |
| ins_cost(ALU_COST); |
| |
| format %{ "#@store_fence" %} |
| |
| ins_encode %{ |
| __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); |
| %} |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct membar_release() %{ |
| match(MemBarRelease); |
| ins_cost(ALU_COST); |
| |
| format %{ "#@membar_release\n\t" |
| "fence iorw ow" %} |
| |
| ins_encode %{ |
| __ block_comment("membar_release"); |
| __ membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore); |
| %} |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct membar_storestore() %{ |
| match(MemBarStoreStore); |
| match(StoreStoreFence); |
| ins_cost(ALU_COST); |
| |
| format %{ "MEMBAR-store-store\t#@membar_storestore" %} |
| |
| ins_encode %{ |
| __ membar(MacroAssembler::StoreStore); |
| %} |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct membar_release_lock() %{ |
| match(MemBarReleaseLock); |
| ins_cost(0); |
| |
| format %{ "#@membar_release_lock (elided)" %} |
| |
| ins_encode %{ |
| __ block_comment("membar_release_lock (elided)"); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct membar_volatile() %{ |
| match(MemBarVolatile); |
| ins_cost(ALU_COST); |
| |
| format %{ "#@membar_volatile\n\t" |
| "fence iorw iorw"%} |
| |
| ins_encode %{ |
| __ block_comment("membar_volatile"); |
| __ membar(MacroAssembler::StoreLoad); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| instruct spin_wait() %{ |
| predicate(UseZihintpause); |
| match(OnSpinWait); |
| ins_cost(CACHE_MISS_COST); |
| |
| format %{ "spin_wait" %} |
| |
| ins_encode %{ |
| __ pause(); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| // ============================================================================ |
| // Cast Instructions (Java-level type cast) |
| |
| instruct castX2P(iRegPNoSp dst, iRegL src) %{ |
| match(Set dst (CastX2P src)); |
| |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $src\t# long -> ptr, #@castX2P" %} |
| |
| ins_encode %{ |
| if ($dst$$reg != $src$$reg) { |
| __ mv(as_Register($dst$$reg), as_Register($src$$reg)); |
| } |
| %} |
| |
| ins_pipe(ialu_reg); |
| %} |
| |
| instruct castP2X(iRegLNoSp dst, iRegP src) %{ |
| match(Set dst (CastP2X src)); |
| |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $src\t# ptr -> long, #@castP2X" %} |
| |
| ins_encode %{ |
| if ($dst$$reg != $src$$reg) { |
| __ mv(as_Register($dst$$reg), as_Register($src$$reg)); |
| } |
| %} |
| |
| ins_pipe(ialu_reg); |
| %} |
| |
| instruct castPP(iRegPNoSp dst) |
| %{ |
| match(Set dst (CastPP dst)); |
| ins_cost(0); |
| |
| size(0); |
| format %{ "# castPP of $dst, #@castPP" %} |
| ins_encode(/* empty encoding */); |
| ins_pipe(pipe_class_empty); |
| %} |
| |
| instruct castLL(iRegL dst) |
| %{ |
| match(Set dst (CastLL dst)); |
| |
| size(0); |
| format %{ "# castLL of $dst, #@castLL" %} |
| ins_encode(/* empty encoding */); |
| ins_cost(0); |
| ins_pipe(pipe_class_empty); |
| %} |
| |
| instruct castII(iRegI dst) |
| %{ |
| match(Set dst (CastII dst)); |
| |
| size(0); |
| format %{ "# castII of $dst, #@castII" %} |
| ins_encode(/* empty encoding */); |
| ins_cost(0); |
| ins_pipe(pipe_class_empty); |
| %} |
| |
| instruct checkCastPP(iRegPNoSp dst) |
| %{ |
| match(Set dst (CheckCastPP dst)); |
| |
| size(0); |
| ins_cost(0); |
| format %{ "# checkcastPP of $dst, #@checkCastPP" %} |
| ins_encode(/* empty encoding */); |
| ins_pipe(pipe_class_empty); |
| %} |
| |
| instruct castFF(fRegF dst) |
| %{ |
| match(Set dst (CastFF dst)); |
| |
| size(0); |
| format %{ "# castFF of $dst" %} |
| ins_encode(/* empty encoding */); |
| ins_cost(0); |
| ins_pipe(pipe_class_empty); |
| %} |
| |
| instruct castDD(fRegD dst) |
| %{ |
| match(Set dst (CastDD dst)); |
| |
| size(0); |
| format %{ "# castDD of $dst" %} |
| ins_encode(/* empty encoding */); |
| ins_cost(0); |
| ins_pipe(pipe_class_empty); |
| %} |
| |
| instruct castVV(vReg dst) |
| %{ |
| match(Set dst (CastVV dst)); |
| |
| size(0); |
| format %{ "# castVV of $dst" %} |
| ins_encode(/* empty encoding */); |
| ins_cost(0); |
| ins_pipe(pipe_class_empty); |
| %} |
| |
| // ============================================================================ |
| // Convert Instructions |
| |
| // int to bool |
| instruct convI2Bool(iRegINoSp dst, iRegI src) |
| %{ |
| match(Set dst (Conv2B src)); |
| |
| ins_cost(ALU_COST); |
| format %{ "snez $dst, $src\t#@convI2Bool" %} |
| |
| ins_encode %{ |
| __ snez(as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg); |
| %} |
| |
| // pointer to bool |
| instruct convP2Bool(iRegINoSp dst, iRegP src) |
| %{ |
| match(Set dst (Conv2B src)); |
| |
| ins_cost(ALU_COST); |
| format %{ "snez $dst, $src\t#@convP2Bool" %} |
| |
| ins_encode %{ |
| __ snez(as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg); |
| %} |
| |
| // int <-> long |
| |
| instruct convI2L_reg_reg(iRegLNoSp dst, iRegIorL2I src) |
| %{ |
| match(Set dst (ConvI2L src)); |
| |
| ins_cost(ALU_COST); |
| format %{ "addw $dst, $src, zr\t#@convI2L_reg_reg" %} |
| ins_encode %{ |
| __ sign_extend(as_Register($dst$$reg), as_Register($src$$reg), 32); |
| %} |
| ins_pipe(ialu_reg); |
| %} |
| |
| instruct convL2I_reg(iRegINoSp dst, iRegL src) %{ |
| match(Set dst (ConvL2I src)); |
| |
| ins_cost(ALU_COST); |
| format %{ "addw $dst, $src, zr\t#@convL2I_reg" %} |
| |
| ins_encode %{ |
| __ sign_extend(as_Register($dst$$reg), as_Register($src$$reg), 32); |
| %} |
| |
| ins_pipe(ialu_reg); |
| %} |
| |
| // int to unsigned long (Zero-extend) |
| instruct convI2UL_reg_reg(iRegLNoSp dst, iRegIorL2I src, immL_32bits mask) |
| %{ |
| match(Set dst (AndL (ConvI2L src) mask)); |
| |
| ins_cost(ALU_COST * 2); |
| format %{ "zero_extend $dst, $src, 32\t# i2ul, #@convI2UL_reg_reg" %} |
| |
| ins_encode %{ |
| __ zero_extend(as_Register($dst$$reg), as_Register($src$$reg), 32); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| // float <-> double |
| |
| instruct convF2D_reg(fRegD dst, fRegF src) %{ |
| match(Set dst (ConvF2D src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.d.s $dst, $src\t#@convF2D_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_d_s(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_f2d); |
| %} |
| |
| instruct convD2F_reg(fRegF dst, fRegD src) %{ |
| match(Set dst (ConvD2F src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.s.d $dst, $src\t#@convD2F_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_s_d(as_FloatRegister($dst$$reg), as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_d2f); |
| %} |
| |
| // float <-> int |
| |
| instruct convF2I_reg_reg(iRegINoSp dst, fRegF src) %{ |
| match(Set dst (ConvF2I src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.w.s $dst, $src\t#@convF2I_reg_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_w_s_safe($dst$$Register, $src$$FloatRegister); |
| %} |
| |
| ins_pipe(fp_f2i); |
| %} |
| |
| instruct convI2F_reg_reg(fRegF dst, iRegIorL2I src) %{ |
| match(Set dst (ConvI2F src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.s.w $dst, $src\t#@convI2F_reg_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_s_w(as_FloatRegister($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(fp_i2f); |
| %} |
| |
| // float <-> long |
| |
| instruct convF2L_reg_reg(iRegLNoSp dst, fRegF src) %{ |
| match(Set dst (ConvF2L src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.l.s $dst, $src\t#@convF2L_reg_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_l_s_safe($dst$$Register, $src$$FloatRegister); |
| %} |
| |
| ins_pipe(fp_f2l); |
| %} |
| |
| instruct convL2F_reg_reg(fRegF dst, iRegL src) %{ |
| match(Set dst (ConvL2F src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.s.l $dst, $src\t#@convL2F_reg_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_s_l(as_FloatRegister($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(fp_l2f); |
| %} |
| |
| // double <-> int |
| |
| instruct convD2I_reg_reg(iRegINoSp dst, fRegD src) %{ |
| match(Set dst (ConvD2I src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.w.d $dst, $src\t#@convD2I_reg_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_w_d_safe($dst$$Register, $src$$FloatRegister); |
| %} |
| |
| ins_pipe(fp_d2i); |
| %} |
| |
| instruct convI2D_reg_reg(fRegD dst, iRegIorL2I src) %{ |
| match(Set dst (ConvI2D src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.d.w $dst, $src\t#@convI2D_reg_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_d_w(as_FloatRegister($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(fp_i2d); |
| %} |
| |
| // double <-> long |
| |
| instruct convD2L_reg_reg(iRegLNoSp dst, fRegD src) %{ |
| match(Set dst (ConvD2L src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.l.d $dst, $src\t#@convD2L_reg_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_l_d_safe($dst$$Register, $src$$FloatRegister); |
| %} |
| |
| ins_pipe(fp_d2l); |
| %} |
| |
| instruct convL2D_reg_reg(fRegD dst, iRegL src) %{ |
| match(Set dst (ConvL2D src)); |
| |
| ins_cost(XFER_COST); |
| format %{ "fcvt.d.l $dst, $src\t#@convL2D_reg_reg" %} |
| |
| ins_encode %{ |
| __ fcvt_d_l(as_FloatRegister($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(fp_l2d); |
| %} |
| |
| // Convert oop into int for vectors alignment masking |
| instruct convP2I(iRegINoSp dst, iRegP src) %{ |
| match(Set dst (ConvL2I (CastP2X src))); |
| |
| ins_cost(ALU_COST * 2); |
| format %{ "zero_extend $dst, $src, 32\t# ptr -> int, #@convP2I" %} |
| |
| ins_encode %{ |
| __ zero_extend($dst$$Register, $src$$Register, 32); |
| %} |
| |
| ins_pipe(ialu_reg); |
| %} |
| |
| // Convert compressed oop into int for vectors alignment masking |
| // in case of 32bit oops (heap < 4Gb). |
| instruct convN2I(iRegINoSp dst, iRegN src) |
| %{ |
| predicate(CompressedOops::shift() == 0); |
| match(Set dst (ConvL2I (CastP2X (DecodeN src)))); |
| |
| ins_cost(ALU_COST); |
| format %{ "mv $dst, $src\t# compressed ptr -> int, #@convN2I" %} |
| |
| ins_encode %{ |
| __ mv($dst$$Register, $src$$Register); |
| %} |
| |
| ins_pipe(ialu_reg); |
| %} |
| |
| instruct round_double_reg(iRegLNoSp dst, fRegD src, fRegD ftmp) %{ |
| match(Set dst (RoundD src)); |
| |
| ins_cost(XFER_COST + BRANCH_COST); |
| effect(TEMP ftmp); |
| format %{ "java_round_double $dst, $src\t#@round_double_reg" %} |
| |
| ins_encode %{ |
| __ java_round_double($dst$$Register, as_FloatRegister($src$$reg), as_FloatRegister($ftmp$$reg)); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| instruct round_float_reg(iRegINoSp dst, fRegF src, fRegF ftmp) %{ |
| match(Set dst (RoundF src)); |
| |
| ins_cost(XFER_COST + BRANCH_COST); |
| effect(TEMP ftmp); |
| format %{ "java_round_float $dst, $src\t#@round_float_reg" %} |
| |
| ins_encode %{ |
| __ java_round_float($dst$$Register, as_FloatRegister($src$$reg), as_FloatRegister($ftmp$$reg)); |
| %} |
| |
| ins_pipe(pipe_slow); |
| %} |
| |
| // Convert oop pointer into compressed form |
| instruct encodeHeapOop(iRegNNoSp dst, iRegP src) %{ |
| match(Set dst (EncodeP src)); |
| ins_cost(ALU_COST); |
| format %{ "encode_heap_oop $dst, $src\t#@encodeHeapOop" %} |
| ins_encode %{ |
| Register s = $src$$Register; |
| Register d = $dst$$Register; |
| __ encode_heap_oop(d, s); |
| %} |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct decodeHeapOop(iRegPNoSp dst, iRegN src) %{ |
| predicate(n->bottom_type()->is_ptr()->ptr() != TypePtr::NotNull && |
| n->bottom_type()->is_ptr()->ptr() != TypePtr::Constant); |
| match(Set dst (DecodeN src)); |
| |
| ins_cost(0); |
| format %{ "decode_heap_oop $dst, $src\t#@decodeHeapOop" %} |
| ins_encode %{ |
| Register s = $src$$Register; |
| Register d = $dst$$Register; |
| __ decode_heap_oop(d, s); |
| %} |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct decodeHeapOop_not_null(iRegPNoSp dst, iRegN src) %{ |
| predicate(n->bottom_type()->is_ptr()->ptr() == TypePtr::NotNull || |
| n->bottom_type()->is_ptr()->ptr() == TypePtr::Constant); |
| match(Set dst (DecodeN src)); |
| |
| ins_cost(0); |
| format %{ "decode_heap_oop_not_null $dst, $src\t#@decodeHeapOop_not_null" %} |
| ins_encode %{ |
| Register s = $src$$Register; |
| Register d = $dst$$Register; |
| __ decode_heap_oop_not_null(d, s); |
| %} |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // Convert klass pointer into compressed form. |
| instruct encodeKlass_not_null(iRegNNoSp dst, iRegP src) %{ |
| match(Set dst (EncodePKlass src)); |
| |
| ins_cost(ALU_COST); |
| format %{ "encode_klass_not_null $dst, $src\t#@encodeKlass_not_null" %} |
| |
| ins_encode %{ |
| Register src_reg = as_Register($src$$reg); |
| Register dst_reg = as_Register($dst$$reg); |
| __ encode_klass_not_null(dst_reg, src_reg, t0); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct decodeKlass_not_null(iRegPNoSp dst, iRegN src, iRegPNoSp tmp) %{ |
| match(Set dst (DecodeNKlass src)); |
| |
| effect(TEMP tmp); |
| |
| ins_cost(ALU_COST); |
| format %{ "decode_klass_not_null $dst, $src\t#@decodeKlass_not_null" %} |
| |
| ins_encode %{ |
| Register src_reg = as_Register($src$$reg); |
| Register dst_reg = as_Register($dst$$reg); |
| Register tmp_reg = as_Register($tmp$$reg); |
| __ decode_klass_not_null(dst_reg, src_reg, tmp_reg); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| // stack <-> reg and reg <-> reg shuffles with no conversion |
| |
| instruct MoveF2I_stack_reg(iRegINoSp dst, stackSlotF src) %{ |
| |
| match(Set dst (MoveF2I src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(LOAD_COST); |
| |
| format %{ "lw $dst, $src\t#@MoveF2I_stack_reg" %} |
| |
| ins_encode %{ |
| __ lw(as_Register($dst$$reg), Address(sp, $src$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_reg); |
| |
| %} |
| |
| instruct MoveI2F_stack_reg(fRegF dst, stackSlotI src) %{ |
| |
| match(Set dst (MoveI2F src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(LOAD_COST); |
| |
| format %{ "flw $dst, $src\t#@MoveI2F_stack_reg" %} |
| |
| ins_encode %{ |
| __ flw(as_FloatRegister($dst$$reg), Address(sp, $src$$disp)); |
| %} |
| |
| ins_pipe(fp_load_mem_s); |
| |
| %} |
| |
| instruct MoveD2L_stack_reg(iRegLNoSp dst, stackSlotD src) %{ |
| |
| match(Set dst (MoveD2L src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(LOAD_COST); |
| |
| format %{ "ld $dst, $src\t#@MoveD2L_stack_reg" %} |
| |
| ins_encode %{ |
| __ ld(as_Register($dst$$reg), Address(sp, $src$$disp)); |
| %} |
| |
| ins_pipe(iload_reg_reg); |
| |
| %} |
| |
| instruct MoveL2D_stack_reg(fRegD dst, stackSlotL src) %{ |
| |
| match(Set dst (MoveL2D src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(LOAD_COST); |
| |
| format %{ "fld $dst, $src\t#@MoveL2D_stack_reg" %} |
| |
| ins_encode %{ |
| __ fld(as_FloatRegister($dst$$reg), Address(sp, $src$$disp)); |
| %} |
| |
| ins_pipe(fp_load_mem_d); |
| |
| %} |
| |
| instruct MoveF2I_reg_stack(stackSlotI dst, fRegF src) %{ |
| |
| match(Set dst (MoveF2I src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(STORE_COST); |
| |
| format %{ "fsw $src, $dst\t#@MoveF2I_reg_stack" %} |
| |
| ins_encode %{ |
| __ fsw(as_FloatRegister($src$$reg), Address(sp, $dst$$disp)); |
| %} |
| |
| ins_pipe(fp_store_reg_s); |
| |
| %} |
| |
| instruct MoveI2F_reg_stack(stackSlotF dst, iRegI src) %{ |
| |
| match(Set dst (MoveI2F src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(STORE_COST); |
| |
| format %{ "sw $src, $dst\t#@MoveI2F_reg_stack" %} |
| |
| ins_encode %{ |
| __ sw(as_Register($src$$reg), Address(sp, $dst$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_reg); |
| |
| %} |
| |
| instruct MoveD2L_reg_stack(stackSlotL dst, fRegD src) %{ |
| |
| match(Set dst (MoveD2L src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(STORE_COST); |
| |
| format %{ "fsd $dst, $src\t#@MoveD2L_reg_stack" %} |
| |
| ins_encode %{ |
| __ fsd(as_FloatRegister($src$$reg), Address(sp, $dst$$disp)); |
| %} |
| |
| ins_pipe(fp_store_reg_d); |
| |
| %} |
| |
| instruct MoveL2D_reg_stack(stackSlotD dst, iRegL src) %{ |
| |
| match(Set dst (MoveL2D src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(STORE_COST); |
| |
| format %{ "sd $src, $dst\t#@MoveL2D_reg_stack" %} |
| |
| ins_encode %{ |
| __ sd(as_Register($src$$reg), Address(sp, $dst$$disp)); |
| %} |
| |
| ins_pipe(istore_reg_reg); |
| |
| %} |
| |
| instruct MoveF2I_reg_reg(iRegINoSp dst, fRegF src) %{ |
| |
| match(Set dst (MoveF2I src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(FMVX_COST); |
| |
| format %{ "fmv.x.w $dst, $src\t#@MoveL2D_reg_stack" %} |
| |
| ins_encode %{ |
| __ fmv_x_w(as_Register($dst$$reg), as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_f2i); |
| |
| %} |
| |
| instruct MoveI2F_reg_reg(fRegF dst, iRegI src) %{ |
| |
| match(Set dst (MoveI2F src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(FMVX_COST); |
| |
| format %{ "fmv.w.x $dst, $src\t#@MoveI2F_reg_reg" %} |
| |
| ins_encode %{ |
| __ fmv_w_x(as_FloatRegister($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(fp_i2f); |
| |
| %} |
| |
| instruct MoveD2L_reg_reg(iRegLNoSp dst, fRegD src) %{ |
| |
| match(Set dst (MoveD2L src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(FMVX_COST); |
| |
| format %{ "fmv.x.d $dst, $src\t#@MoveD2L_reg_reg" %} |
| |
| ins_encode %{ |
| __ fmv_x_d(as_Register($dst$$reg), as_FloatRegister($src$$reg)); |
| %} |
| |
| ins_pipe(fp_d2l); |
| |
| %} |
| |
| instruct MoveL2D_reg_reg(fRegD dst, iRegL src) %{ |
| |
| match(Set dst (MoveL2D src)); |
| |
| effect(DEF dst, USE src); |
| |
| ins_cost(FMVX_COST); |
| |
| format %{ "fmv.d.x $dst, $src\t#@MoveD2L_reg_reg" %} |
| |
| ins_encode %{ |
| __ fmv_d_x(as_FloatRegister($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(fp_l2d); |
| |
| %} |
| |
| // ============================================================================ |
| // Compare Instructions which set the result float comparisons in dest register. |
| |
| instruct cmpF3_reg_reg(iRegINoSp dst, fRegF op1, fRegF op2) |
| %{ |
| match(Set dst (CmpF3 op1 op2)); |
| |
| ins_cost(XFER_COST * 2 + BRANCH_COST + ALU_COST); |
| format %{ "flt.s $dst, $op2, $op1\t#@cmpF3_reg_reg\n\t" |
| "bgtz $dst, done\n\t" |
| "feq.s $dst, $op1, $op2\n\t" |
| "addi $dst, $dst, -1\t#@cmpF3_reg_reg" |
| %} |
| |
| ins_encode %{ |
| // we want -1 for unordered or less than, 0 for equal and 1 for greater than. |
| __ float_compare(as_Register($dst$$reg), as_FloatRegister($op1$$reg), |
| as_FloatRegister($op2$$reg), -1 /*unordered_result < 0*/); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct cmpD3_reg_reg(iRegINoSp dst, fRegD op1, fRegD op2) |
| %{ |
| match(Set dst (CmpD3 op1 op2)); |
| |
| ins_cost(XFER_COST * 2 + BRANCH_COST + ALU_COST); |
| format %{ "flt.d $dst, $op2, $op1\t#@cmpD3_reg_reg\n\t" |
| "bgtz $dst, done\n\t" |
| "feq.d $dst, $op1, $op2\n\t" |
| "addi $dst, $dst, -1\t#@cmpD3_reg_reg" |
| %} |
| |
| ins_encode %{ |
| // we want -1 for unordered or less than, 0 for equal and 1 for greater than. |
| __ double_compare(as_Register($dst$$reg), as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), -1 /*unordered_result < 0*/); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct cmpL3_reg_reg(iRegINoSp dst, iRegL op1, iRegL op2) |
| %{ |
| match(Set dst (CmpL3 op1 op2)); |
| |
| ins_cost(ALU_COST * 3 + BRANCH_COST); |
| format %{ "slt $dst, $op2, $op1\t#@cmpL3_reg_reg\n\t" |
| "bnez $dst, done\n\t" |
| "slt $dst, $op1, $op2\n\t" |
| "neg $dst, $dst\t#@cmpL3_reg_reg" |
| %} |
| ins_encode %{ |
| __ cmp_l2i(t0, as_Register($op1$$reg), as_Register($op2$$reg)); |
| __ mv(as_Register($dst$$reg), t0); |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| instruct cmpLTMask_reg_reg(iRegINoSp dst, iRegI p, iRegI q) |
| %{ |
| match(Set dst (CmpLTMask p q)); |
| |
| ins_cost(2 * ALU_COST); |
| |
| format %{ "slt $dst, $p, $q\t#@cmpLTMask_reg_reg\n\t" |
| "subw $dst, zr, $dst\t#@cmpLTMask_reg_reg" |
| %} |
| |
| ins_encode %{ |
| __ slt(as_Register($dst$$reg), as_Register($p$$reg), as_Register($q$$reg)); |
| __ subw(as_Register($dst$$reg), zr, as_Register($dst$$reg)); |
| %} |
| |
| ins_pipe(ialu_reg_reg); |
| %} |
| |
| instruct cmpLTMask_reg_zero(iRegINoSp dst, iRegIorL2I op, immI0 zero) |
| %{ |
| match(Set dst (CmpLTMask op zero)); |
| |
| ins_cost(ALU_COST); |
| |
| format %{ "sraiw $dst, $dst, 31\t#@cmpLTMask_reg_reg" %} |
| |
| ins_encode %{ |
| __ sraiw(as_Register($dst$$reg), as_Register($op$$reg), 31); |
| %} |
| |
| ins_pipe(ialu_reg_shift); |
| %} |
| |
| |
| // ============================================================================ |
| // Max and Min |
| |
| instruct minI_reg_reg(iRegINoSp dst, iRegI src) |
| %{ |
| match(Set dst (MinI dst src)); |
| |
| ins_cost(BRANCH_COST + ALU_COST); |
| format %{ |
| "ble $dst, $src, skip\t#@minI_reg_reg\n\t" |
| "mv $dst, $src\n\t" |
| "skip:" |
| %} |
| |
| ins_encode %{ |
| Label Lskip; |
| __ ble(as_Register($dst$$reg), as_Register($src$$reg), Lskip); |
| __ mv(as_Register($dst$$reg), as_Register($src$$reg)); |
| __ bind(Lskip); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct maxI_reg_reg(iRegINoSp dst, iRegI src) |
| %{ |
| match(Set dst (MaxI dst src)); |
| |
| ins_cost(BRANCH_COST + ALU_COST); |
| format %{ |
| "bge $dst, $src, skip\t#@maxI_reg_reg\n\t" |
| "mv $dst, $src\n\t" |
| "skip:" |
| %} |
| |
| ins_encode %{ |
| Label Lskip; |
| __ bge(as_Register($dst$$reg), as_Register($src$$reg), Lskip); |
| __ mv(as_Register($dst$$reg), as_Register($src$$reg)); |
| __ bind(Lskip); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| // special case for comparing with zero |
| // n.b. this is selected in preference to the rule above because it |
| // avoids loading constant 0 into a source register |
| |
| instruct minI_reg_zero(iRegINoSp dst, immI0 zero) |
| %{ |
| match(Set dst (MinI dst zero)); |
| match(Set dst (MinI zero dst)); |
| |
| ins_cost(BRANCH_COST + ALU_COST); |
| format %{ |
| "blez $dst, skip\t#@minI_reg_zero\n\t" |
| "mv $dst, zr\n\t" |
| "skip:" |
| %} |
| |
| ins_encode %{ |
| Label Lskip; |
| __ blez(as_Register($dst$$reg), Lskip); |
| __ mv(as_Register($dst$$reg), zr); |
| __ bind(Lskip); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct maxI_reg_zero(iRegINoSp dst, immI0 zero) |
| %{ |
| match(Set dst (MaxI dst zero)); |
| match(Set dst (MaxI zero dst)); |
| |
| ins_cost(BRANCH_COST + ALU_COST); |
| format %{ |
| "bgez $dst, skip\t#@maxI_reg_zero\n\t" |
| "mv $dst, zr\n\t" |
| "skip:" |
| %} |
| |
| ins_encode %{ |
| Label Lskip; |
| __ bgez(as_Register($dst$$reg), Lskip); |
| __ mv(as_Register($dst$$reg), zr); |
| __ bind(Lskip); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct minI_rReg(iRegINoSp dst, iRegI src1, iRegI src2) |
| %{ |
| match(Set dst (MinI src1 src2)); |
| |
| effect(DEF dst, USE src1, USE src2); |
| |
| ins_cost(BRANCH_COST + ALU_COST * 2); |
| format %{ |
| "ble $src1, $src2, Lsrc1.\t#@minI_rReg\n\t" |
| "mv $dst, $src2\n\t" |
| "j Ldone\n\t" |
| "bind Lsrc1\n\t" |
| "mv $dst, $src1\n\t" |
| "bind\t#@minI_rReg" |
| %} |
| |
| ins_encode %{ |
| Label Lsrc1, Ldone; |
| __ ble(as_Register($src1$$reg), as_Register($src2$$reg), Lsrc1); |
| __ mv(as_Register($dst$$reg), as_Register($src2$$reg)); |
| __ j(Ldone); |
| __ bind(Lsrc1); |
| __ mv(as_Register($dst$$reg), as_Register($src1$$reg)); |
| __ bind(Ldone); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct maxI_rReg(iRegINoSp dst, iRegI src1, iRegI src2) |
| %{ |
| match(Set dst (MaxI src1 src2)); |
| |
| effect(DEF dst, USE src1, USE src2); |
| |
| ins_cost(BRANCH_COST + ALU_COST * 2); |
| format %{ |
| "bge $src1, $src2, Lsrc1\t#@maxI_rReg\n\t" |
| "mv $dst, $src2\n\t" |
| "j Ldone\n\t" |
| "bind Lsrc1\n\t" |
| "mv $dst, $src1\n\t" |
| "bind\t#@maxI_rReg" |
| %} |
| |
| ins_encode %{ |
| Label Lsrc1, Ldone; |
| __ bge(as_Register($src1$$reg), as_Register($src2$$reg), Lsrc1); |
| __ mv(as_Register($dst$$reg), as_Register($src2$$reg)); |
| __ j(Ldone); |
| __ bind(Lsrc1); |
| __ mv(as_Register($dst$$reg), as_Register($src1$$reg)); |
| __ bind(Ldone); |
| |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| // ============================================================================ |
| // Branch Instructions |
| // Direct Branch. |
| instruct branch(label lbl) |
| %{ |
| match(Goto); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| format %{ "j $lbl\t#@branch" %} |
| |
| ins_encode(riscv_enc_j(lbl)); |
| |
| ins_pipe(pipe_branch); |
| %} |
| |
| // ============================================================================ |
| // Compare and Branch Instructions |
| |
| // Patterns for short (< 12KiB) variants |
| |
| // Compare flags and branch near instructions. |
| instruct cmpFlag_branch(cmpOpEqNe cmp, rFlagsReg cr, label lbl) %{ |
| match(If cmp cr); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| format %{ "b$cmp $cr, zr, $lbl\t#@cmpFlag_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($cr$$reg), *($lbl$$label)); |
| %} |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare signed int and branch near instructions |
| instruct cmpI_branch(cmpOp cmp, iRegI op1, iRegI op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpI_branch'. |
| match(If cmp (CmpI op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, $op2, $lbl\t#@cmpI_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| ins_short_branch(1); |
| %} |
| |
| instruct cmpI_loop(cmpOp cmp, iRegI op1, iRegI op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpI_loop'. |
| match(CountedLoopEnd cmp (CmpI op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, $op2, $lbl\t#@cmpI_loop" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare unsigned int and branch near instructions |
| instruct cmpU_branch(cmpOpU cmp, iRegI op1, iRegI op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpU_branch'. |
| match(If cmp (CmpU op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, $op2, $lbl\t#@cmpU_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), |
| as_Register($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare signed long and branch near instructions |
| instruct cmpL_branch(cmpOp cmp, iRegL op1, iRegL op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpL_branch'. |
| match(If cmp (CmpL op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, $op2, $lbl\t#@cmpL_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| ins_short_branch(1); |
| %} |
| |
| instruct cmpL_loop(cmpOp cmp, iRegL op1, iRegL op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpL_loop'. |
| match(CountedLoopEnd cmp (CmpL op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, $op2, $lbl\t#@cmpL_loop" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare unsigned long and branch near instructions |
| instruct cmpUL_branch(cmpOpU cmp, iRegL op1, iRegL op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpUL_branch'. |
| match(If cmp (CmpUL op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| format %{ "b$cmp $op1, $op2, $lbl\t#@cmpUL_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), |
| as_Register($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare pointer and branch near instructions |
| instruct cmpP_branch(cmpOpU cmp, iRegP op1, iRegP op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpP_branch'. |
| match(If cmp (CmpP op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, $op2, $lbl\t#@cmpP_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), |
| as_Register($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare narrow pointer and branch near instructions |
| instruct cmpN_branch(cmpOpU cmp, iRegN op1, iRegN op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpN_branch'. |
| match(If cmp (CmpN op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, $op2, $lbl\t#@cmpN_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), |
| as_Register($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare float and branch near instructions |
| instruct cmpF_branch(cmpOp cmp, fRegF op1, fRegF op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpF_branch'. |
| match(If cmp (CmpF op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(XFER_COST + BRANCH_COST); |
| format %{ "float_b$cmp $op1, $op2, $lbl \t#@cmpF_branch"%} |
| |
| ins_encode %{ |
| __ float_cmp_branch($cmp$$cmpcode, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| ins_short_branch(1); |
| %} |
| |
| // Compare double and branch near instructions |
| instruct cmpD_branch(cmpOp cmp, fRegD op1, fRegD op2, label lbl) |
| %{ |
| // Same match rule as `far_cmpD_branch'. |
| match(If cmp (CmpD op1 op2)); |
| effect(USE lbl); |
| |
| ins_cost(XFER_COST + BRANCH_COST); |
| format %{ "double_b$cmp $op1, $op2, $lbl\t#@cmpD_branch"%} |
| |
| ins_encode %{ |
| __ float_cmp_branch($cmp$$cmpcode | C2_MacroAssembler::double_branch_mask, as_FloatRegister($op1$$reg), |
| as_FloatRegister($op2$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| ins_short_branch(1); |
| %} |
| |
| // Compare signed int with zero and branch near instructions |
| instruct cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI0 zero, label lbl) |
| %{ |
| // Same match rule as `far_cmpI_reg_imm0_branch'. |
| match(If cmp (CmpI op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| format %{ "b$cmp $op1, zr, $lbl\t#@cmpI_reg_imm0_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| instruct cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI0 zero, label lbl) |
| %{ |
| // Same match rule as `far_cmpI_reg_imm0_loop'. |
| match(CountedLoopEnd cmp (CmpI op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, zr, $lbl\t#@cmpI_reg_imm0_loop" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare unsigned int with zero and branch near instructions |
| instruct cmpUEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, label lbl) |
| %{ |
| // Same match rule as `far_cmpUEqNeLeGt_reg_imm0_branch'. |
| match(If cmp (CmpU op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, zr, $lbl\t#@cmpUEqNeLeGt_reg_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare signed long with zero and branch near instructions |
| instruct cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL0 zero, label lbl) |
| %{ |
| // Same match rule as `far_cmpL_reg_imm0_branch'. |
| match(If cmp (CmpL op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, zr, $lbl\t#@cmpL_reg_imm0_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| instruct cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL0 zero, label lbl) |
| %{ |
| // Same match rule as `far_cmpL_reg_imm0_loop'. |
| match(CountedLoopEnd cmp (CmpL op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, zr, $lbl\t#@cmpL_reg_imm0_loop" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare unsigned long with zero and branch near instructions |
| instruct cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 zero, label lbl) |
| %{ |
| // Same match rule as `far_cmpULEqNeLeGt_reg_imm0_branch'. |
| match(If cmp (CmpUL op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, zr, $lbl\t#@cmpULEqNeLeGt_reg_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare pointer with zero and branch near instructions |
| instruct cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) %{ |
| // Same match rule as `far_cmpP_reg_imm0_branch'. |
| match(If cmp (CmpP op1 zero)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| format %{ "b$cmp $op1, zr, $lbl\t#@cmpP_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare narrow pointer with zero and branch near instructions |
| instruct cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) %{ |
| // Same match rule as `far_cmpN_reg_imm0_branch'. |
| match(If cmp (CmpN op1 zero)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "b$cmp $op1, zr, $lbl\t#@cmpN_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Compare narrow pointer with pointer zero and branch near instructions |
| instruct cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP0 zero, label lbl) %{ |
| // Same match rule as `far_cmpP_narrowOop_imm0_branch'. |
| match(If cmp (CmpP (DecodeN op1) zero)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| format %{ "b$cmp $op1, zr, $lbl\t#@cmpP_narrowOop_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label)); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| ins_short_branch(1); |
| %} |
| |
| // Patterns for far (20KiB) variants |
| |
| instruct far_cmpFlag_branch(cmpOp cmp, rFlagsReg cr, label lbl) %{ |
| match(If cmp cr); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| format %{ "far_b$cmp $cr, zr, $lbl\t#@far_cmpFlag_branch"%} |
| |
| ins_encode %{ |
| __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($cr$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| // Compare signed int and branch far instructions |
| instruct far_cmpI_branch(cmpOp cmp, iRegI op1, iRegI op2, label lbl) %{ |
| match(If cmp (CmpI op1 op2)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| // the format instruction [far_b$cmp] here is be used as two insructions |
| // in macroassembler: b$not_cmp(op1, op2, done), j($lbl), bind(done) |
| format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpI_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| %} |
| |
| instruct far_cmpI_loop(cmpOp cmp, iRegI op1, iRegI op2, label lbl) %{ |
| match(CountedLoopEnd cmp (CmpI op1 op2)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpI_loop" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| %} |
| |
| instruct far_cmpU_branch(cmpOpU cmp, iRegI op1, iRegI op2, label lbl) %{ |
| match(If cmp (CmpU op1 op2)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpU_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), |
| as_Register($op2$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| %} |
| |
| instruct far_cmpL_branch(cmpOp cmp, iRegL op1, iRegL op2, label lbl) %{ |
| match(If cmp (CmpL op1 op2)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpL_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| %} |
| |
| instruct far_cmpLloop(cmpOp cmp, iRegL op1, iRegL op2, label lbl) %{ |
| match(CountedLoopEnd cmp (CmpL op1 op2)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpL_loop" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), as_Register($op2$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| %} |
| |
| instruct far_cmpUL_branch(cmpOpU cmp, iRegL op1, iRegL op2, label lbl) %{ |
| match(If cmp (CmpUL op1 op2)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpUL_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), |
| as_Register($op2$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| %} |
| |
| instruct far_cmpP_branch(cmpOpU cmp, iRegP op1, iRegP op2, label lbl) |
| %{ |
| match(If cmp (CmpP op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpP_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), |
| as_Register($op2$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| %} |
| |
| instruct far_cmpN_branch(cmpOpU cmp, iRegN op1, iRegN op2, label lbl) |
| %{ |
| match(If cmp (CmpN op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| format %{ "far_b$cmp $op1, $op2, $lbl\t#@far_cmpN_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, as_Register($op1$$reg), |
| as_Register($op2$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmp_branch); |
| %} |
| |
| // Float compare and branch instructions |
| instruct far_cmpF_branch(cmpOp cmp, fRegF op1, fRegF op2, label lbl) |
| %{ |
| match(If cmp (CmpF op1 op2)); |
| |
| effect(USE lbl); |
| |
| ins_cost(XFER_COST + BRANCH_COST * 2); |
| format %{ "far_float_b$cmp $op1, $op2, $lbl\t#@far_cmpF_branch"%} |
| |
| ins_encode %{ |
| __ float_cmp_branch($cmp$$cmpcode, as_FloatRegister($op1$$reg), as_FloatRegister($op2$$reg), |
| *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| // Double compare and branch instructions |
| instruct far_cmpD_branch(cmpOp cmp, fRegD op1, fRegD op2, label lbl) |
| %{ |
| match(If cmp (CmpD op1 op2)); |
| effect(USE lbl); |
| |
| ins_cost(XFER_COST + BRANCH_COST * 2); |
| format %{ "far_double_b$cmp $op1, $op2, $lbl\t#@far_cmpD_branch"%} |
| |
| ins_encode %{ |
| __ float_cmp_branch($cmp$$cmpcode | C2_MacroAssembler::double_branch_mask, as_FloatRegister($op1$$reg), |
| as_FloatRegister($op2$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct far_cmpI_reg_imm0_branch(cmpOp cmp, iRegI op1, immI0 zero, label lbl) |
| %{ |
| match(If cmp (CmpI op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| format %{ "far_b$cmp $op1, zr, $lbl\t#@far_cmpI_reg_imm0_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| instruct far_cmpI_reg_imm0_loop(cmpOp cmp, iRegI op1, immI0 zero, label lbl) |
| %{ |
| match(CountedLoopEnd cmp (CmpI op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| format %{ "far_b$cmp $op1, zr, $lbl\t#@far_cmpI_reg_imm0_loop" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| instruct far_cmpUEqNeLeGt_imm0_branch(cmpOpUEqNeLeGt cmp, iRegI op1, immI0 zero, label lbl) |
| %{ |
| match(If cmp (CmpU op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| format %{ "far_b$cmp $op1, zr, $lbl\t#@far_cmpUEqNeLeGt_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| // compare lt/ge unsigned instructs has no short instruct with same match |
| instruct far_cmpULtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegI op1, immI0 zero, label lbl) |
| %{ |
| match(If cmp (CmpU op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "j $lbl if $cmp == ge\t#@far_cmpULtGe_reg_imm0_branch" %} |
| |
| ins_encode(riscv_enc_far_cmpULtGe_imm0_branch(cmp, op1, lbl)); |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| instruct far_cmpL_reg_imm0_branch(cmpOp cmp, iRegL op1, immL0 zero, label lbl) |
| %{ |
| match(If cmp (CmpL op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| format %{ "far_b$cmp $op1, zr, $lbl\t#@far_cmpL_reg_imm0_branch" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| instruct far_cmpL_reg_imm0_loop(cmpOp cmp, iRegL op1, immL0 zero, label lbl) |
| %{ |
| match(CountedLoopEnd cmp (CmpL op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| format %{ "far_b$cmp $op1, zr, $lbl\t#@far_cmpL_reg_imm0_loop" %} |
| |
| ins_encode %{ |
| __ cmp_branch($cmp$$cmpcode, as_Register($op1$$reg), zr, *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| instruct far_cmpULEqNeLeGt_reg_imm0_branch(cmpOpUEqNeLeGt cmp, iRegL op1, immL0 zero, label lbl) |
| %{ |
| match(If cmp (CmpUL op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| format %{ "far_b$cmp $op1, zr, $lbl\t#@far_cmpULEqNeLeGt_reg_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpUEqNeLeGt_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| // compare lt/ge unsigned instructs has no short instruct with same match |
| instruct far_cmpULLtGe_reg_imm0_branch(cmpOpULtGe cmp, iRegL op1, immL0 zero, label lbl) |
| %{ |
| match(If cmp (CmpUL op1 zero)); |
| |
| effect(USE op1, USE lbl); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "j $lbl if $cmp == ge\t#@far_cmpULLtGe_reg_imm0_branch" %} |
| |
| ins_encode(riscv_enc_far_cmpULtGe_imm0_branch(cmp, op1, lbl)); |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| instruct far_cmpP_imm0_branch(cmpOpEqNe cmp, iRegP op1, immP0 zero, label lbl) %{ |
| match(If cmp (CmpP op1 zero)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| format %{ "far_b$cmp $op1, zr, $lbl\t#@far_cmpP_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| instruct far_cmpN_imm0_branch(cmpOpEqNe cmp, iRegN op1, immN0 zero, label lbl) %{ |
| match(If cmp (CmpN op1 zero)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| |
| format %{ "far_b$cmp $op1, zr, $lbl\t#@far_cmpN_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| instruct far_cmpP_narrowOop_imm0_branch(cmpOpEqNe cmp, iRegN op1, immP0 zero, label lbl) %{ |
| match(If cmp (CmpP (DecodeN op1) zero)); |
| effect(USE lbl); |
| |
| ins_cost(BRANCH_COST * 2); |
| format %{ "far_b$cmp $op1, zr, $lbl\t#@far_cmpP_narrowOop_imm0_branch" %} |
| |
| ins_encode %{ |
| __ enc_cmpEqNe_imm0_branch($cmp$$cmpcode, as_Register($op1$$reg), *($lbl$$label), /* is_far */ true); |
| %} |
| |
| ins_pipe(pipe_cmpz_branch); |
| %} |
| |
| // ============================================================================ |
| // Conditional Move Instructions |
| instruct cmovI_cmpI(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOp cop) %{ |
| match(Set dst (CMoveI (Binary cop (CmpI op1 op2)) (Binary dst src))); |
| ins_cost(ALU_COST + BRANCH_COST); |
| |
| format %{ |
| "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpI\n\t" |
| %} |
| |
| ins_encode %{ |
| __ enc_cmove($cop$$cmpcode, |
| as_Register($op1$$reg), as_Register($op2$$reg), |
| as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct cmovI_cmpU(iRegINoSp dst, iRegI src, iRegI op1, iRegI op2, cmpOpU cop) %{ |
| match(Set dst (CMoveI (Binary cop (CmpU op1 op2)) (Binary dst src))); |
| ins_cost(ALU_COST + BRANCH_COST); |
| |
| format %{ |
| "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpU\n\t" |
| %} |
| |
| ins_encode %{ |
| __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, |
| as_Register($op1$$reg), as_Register($op2$$reg), |
| as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct cmovI_cmpL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOp cop) %{ |
| match(Set dst (CMoveI (Binary cop (CmpL op1 op2)) (Binary dst src))); |
| ins_cost(ALU_COST + BRANCH_COST); |
| |
| format %{ |
| "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpL\n\t" |
| %} |
| |
| ins_encode %{ |
| __ enc_cmove($cop$$cmpcode, |
| as_Register($op1$$reg), as_Register($op2$$reg), |
| as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct cmovI_cmpUL(iRegINoSp dst, iRegI src, iRegL op1, iRegL op2, cmpOpU cop) %{ |
| match(Set dst (CMoveI (Binary cop (CmpUL op1 op2)) (Binary dst src))); |
| ins_cost(ALU_COST + BRANCH_COST); |
| |
| format %{ |
| "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovI_cmpUL\n\t" |
| %} |
| |
| ins_encode %{ |
| __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, |
| as_Register($op1$$reg), as_Register($op2$$reg), |
| as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct cmovL_cmpL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOp cop) %{ |
| match(Set dst (CMoveL (Binary cop (CmpL op1 op2)) (Binary dst src))); |
| ins_cost(ALU_COST + BRANCH_COST); |
| |
| format %{ |
| "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpL\n\t" |
| %} |
| |
| ins_encode %{ |
| __ enc_cmove($cop$$cmpcode, |
| as_Register($op1$$reg), as_Register($op2$$reg), |
| as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct cmovL_cmpUL(iRegLNoSp dst, iRegL src, iRegL op1, iRegL op2, cmpOpU cop) %{ |
| match(Set dst (CMoveL (Binary cop (CmpUL op1 op2)) (Binary dst src))); |
| ins_cost(ALU_COST + BRANCH_COST); |
| |
| format %{ |
| "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpUL\n\t" |
| %} |
| |
| ins_encode %{ |
| __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, |
| as_Register($op1$$reg), as_Register($op2$$reg), |
| as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct cmovL_cmpI(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOp cop) %{ |
| match(Set dst (CMoveL (Binary cop (CmpI op1 op2)) (Binary dst src))); |
| ins_cost(ALU_COST + BRANCH_COST); |
| |
| format %{ |
| "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpI\n\t" |
| %} |
| |
| ins_encode %{ |
| __ enc_cmove($cop$$cmpcode, |
| as_Register($op1$$reg), as_Register($op2$$reg), |
| as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| instruct cmovL_cmpU(iRegLNoSp dst, iRegL src, iRegI op1, iRegI op2, cmpOpU cop) %{ |
| match(Set dst (CMoveL (Binary cop (CmpU op1 op2)) (Binary dst src))); |
| ins_cost(ALU_COST + BRANCH_COST); |
| |
| format %{ |
| "CMove $dst, ($op1 $cop $op2), $dst, $src\t#@cmovL_cmpU\n\t" |
| %} |
| |
| ins_encode %{ |
| __ enc_cmove($cop$$cmpcode | C2_MacroAssembler::unsigned_branch_mask, |
| as_Register($op1$$reg), as_Register($op2$$reg), |
| as_Register($dst$$reg), as_Register($src$$reg)); |
| %} |
| |
| ins_pipe(pipe_class_compare); |
| %} |
| |
| // ============================================================================ |
| // Procedure Call/Return Instructions |
| |
| // Call Java Static Instruction |
| // Note: If this code changes, the corresponding ret_addr_offset() and |
| // compute_padding() functions will have to be adjusted. |
| instruct CallStaticJavaDirect(method meth) |
| %{ |
| match(CallStaticJava); |
| |
| effect(USE meth); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "CALL,static $meth\t#@CallStaticJavaDirect" %} |
| |
| ins_encode(riscv_enc_java_static_call(meth), |
| riscv_enc_call_epilog); |
| |
| ins_pipe(pipe_class_call); |
| ins_alignment(4); |
| %} |
| |
| // TO HERE |
| |
| // Call Java Dynamic Instruction |
| // Note: If this code changes, the corresponding ret_addr_offset() and |
| // compute_padding() functions will have to be adjusted. |
| instruct CallDynamicJavaDirect(method meth, rFlagsReg cr) |
| %{ |
| match(CallDynamicJava); |
| |
| effect(USE meth, KILL cr); |
| |
| ins_cost(BRANCH_COST + ALU_COST * 6); |
| |
| format %{ "CALL,dynamic $meth\t#@CallDynamicJavaDirect" %} |
| |
| ins_encode(riscv_enc_java_dynamic_call(meth), |
| riscv_enc_call_epilog); |
| |
| ins_pipe(pipe_class_call); |
| ins_alignment(4); |
| %} |
| |
| // Call Runtime Instruction |
| |
| instruct CallRuntimeDirect(method meth, rFlagsReg cr) |
| %{ |
| match(CallRuntime); |
| |
| effect(USE meth, KILL cr); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "CALL, runtime $meth\t#@CallRuntimeDirect" %} |
| |
| ins_encode(riscv_enc_java_to_runtime(meth)); |
| |
| ins_pipe(pipe_class_call); |
| %} |
| |
| // Call Runtime Instruction |
| |
| instruct CallLeafDirect(method meth, rFlagsReg cr) |
| %{ |
| match(CallLeaf); |
| |
| effect(USE meth, KILL cr); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "CALL, runtime leaf $meth\t#@CallLeafDirect" %} |
| |
| ins_encode(riscv_enc_java_to_runtime(meth)); |
| |
| ins_pipe(pipe_class_call); |
| %} |
| |
| // Call Runtime Instruction |
| |
| instruct CallLeafNoFPDirect(method meth, rFlagsReg cr) |
| %{ |
| match(CallLeafNoFP); |
| |
| effect(USE meth, KILL cr); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "CALL, runtime leaf nofp $meth\t#@CallLeafNoFPDirect" %} |
| |
| ins_encode(riscv_enc_java_to_runtime(meth)); |
| |
| ins_pipe(pipe_class_call); |
| %} |
| |
| // ============================================================================ |
| // Partial Subtype Check |
| // |
| // 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 zero for a hit. The encoding |
| // ALSO sets flags. |
| |
| instruct partialSubtypeCheck(iRegP_R15 result, iRegP_R14 sub, iRegP_R10 super, iRegP_R12 tmp, rFlagsReg cr) |
| %{ |
| match(Set result (PartialSubtypeCheck sub super)); |
| effect(KILL tmp, KILL cr); |
| |
| ins_cost(2 * STORE_COST + 3 * LOAD_COST + 4 * ALU_COST + BRANCH_COST * 4); |
| format %{ "partialSubtypeCheck $result, $sub, $super\t#@partialSubtypeCheck" %} |
| |
| ins_encode(riscv_enc_partial_subtype_check(sub, super, tmp, result)); |
| |
| opcode(0x1); // Force zero of result reg on hit |
| |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct partialSubtypeCheckVsZero(iRegP_R15 result, iRegP_R14 sub, iRegP_R10 super, iRegP_R12 tmp, |
| immP0 zero, rFlagsReg cr) |
| %{ |
| match(Set cr (CmpP (PartialSubtypeCheck sub super) zero)); |
| effect(KILL tmp, KILL result); |
| |
| ins_cost(2 * STORE_COST + 3 * LOAD_COST + 4 * ALU_COST + BRANCH_COST * 4); |
| format %{ "partialSubtypeCheck $result, $sub, $super == 0\t#@partialSubtypeCheckVsZero" %} |
| |
| ins_encode(riscv_enc_partial_subtype_check(sub, super, tmp, result)); |
| |
| opcode(0x0); // Don't zero result reg on hit |
| |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_compareU(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2, |
| iRegI_R10 result, iRegP_R28 tmp1, iRegL_R29 tmp2, iRegL_R30 tmp3, rFlagsReg cr) |
| %{ |
| predicate(!UseRVV && ((StrCompNode *)n)->encoding() == StrIntrinsicNode::UU); |
| match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); |
| effect(KILL tmp1, KILL tmp2, KILL tmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); |
| |
| format %{ "String Compare $str1, $cnt1, $str2, $cnt2 -> $result\t#@string_compareU" %} |
| ins_encode %{ |
| // Count is in 8-bit bytes; non-Compact chars are 16 bits. |
| __ string_compare($str1$$Register, $str2$$Register, |
| $cnt1$$Register, $cnt2$$Register, $result$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, |
| StrIntrinsicNode::UU); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_compareL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2, |
| iRegI_R10 result, iRegP_R28 tmp1, iRegL_R29 tmp2, iRegL_R30 tmp3, rFlagsReg cr) |
| %{ |
| predicate(!UseRVV && ((StrCompNode *)n)->encoding() == StrIntrinsicNode::LL); |
| match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); |
| effect(KILL tmp1, KILL tmp2, KILL tmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); |
| |
| format %{ "String Compare $str1, $cnt1, $str2, $cnt2 -> $result\t#@string_compareL" %} |
| ins_encode %{ |
| __ string_compare($str1$$Register, $str2$$Register, |
| $cnt1$$Register, $cnt2$$Register, $result$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, |
| StrIntrinsicNode::LL); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_compareUL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2, |
| iRegI_R10 result, iRegP_R28 tmp1, iRegL_R29 tmp2, iRegL_R30 tmp3, rFlagsReg cr) |
| %{ |
| predicate(!UseRVV && ((StrCompNode *)n)->encoding() == StrIntrinsicNode::UL); |
| match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); |
| effect(KILL tmp1, KILL tmp2, KILL tmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); |
| |
| format %{"String Compare $str1, $cnt1, $str2, $cnt2 -> $result\t#@string_compareUL" %} |
| ins_encode %{ |
| __ string_compare($str1$$Register, $str2$$Register, |
| $cnt1$$Register, $cnt2$$Register, $result$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, |
| StrIntrinsicNode::UL); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_compareLU(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2, |
| iRegI_R10 result, iRegP_R28 tmp1, iRegL_R29 tmp2, iRegL_R30 tmp3, |
| rFlagsReg cr) |
| %{ |
| predicate(!UseRVV && ((StrCompNode *)n)->encoding() == StrIntrinsicNode::LU); |
| match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2))); |
| effect(KILL tmp1, KILL tmp2, KILL tmp3, USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, KILL cr); |
| |
| format %{ "String Compare $str1, $cnt1, $str2, $cnt2 -> $result\t#@string_compareLU" %} |
| ins_encode %{ |
| __ string_compare($str1$$Register, $str2$$Register, |
| $cnt1$$Register, $cnt2$$Register, $result$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, |
| StrIntrinsicNode::LU); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_indexofUU(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2, |
| iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, |
| iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, rFlagsReg cr) |
| %{ |
| predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU); |
| match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); |
| effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, TEMP_DEF result, |
| TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, KILL cr); |
| |
| format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (UU)" %} |
| ins_encode %{ |
| __ string_indexof($str1$$Register, $str2$$Register, |
| $cnt1$$Register, $cnt2$$Register, |
| $tmp1$$Register, $tmp2$$Register, |
| $tmp3$$Register, $tmp4$$Register, |
| $tmp5$$Register, $tmp6$$Register, |
| $result$$Register, StrIntrinsicNode::UU); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_indexofLL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2, |
| iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, |
| iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, rFlagsReg cr) |
| %{ |
| predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL); |
| match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); |
| effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, TEMP_DEF result, |
| TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, KILL cr); |
| |
| format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (LL)" %} |
| ins_encode %{ |
| __ string_indexof($str1$$Register, $str2$$Register, |
| $cnt1$$Register, $cnt2$$Register, |
| $tmp1$$Register, $tmp2$$Register, |
| $tmp3$$Register, $tmp4$$Register, |
| $tmp5$$Register, $tmp6$$Register, |
| $result$$Register, StrIntrinsicNode::LL); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_indexofUL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, iRegI_R14 cnt2, |
| iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, iRegINoSp tmp3, |
| iRegINoSp tmp4, iRegINoSp tmp5, iRegINoSp tmp6, rFlagsReg cr) |
| %{ |
| predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL); |
| match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2))); |
| effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, USE_KILL cnt2, TEMP_DEF result, |
| TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, TEMP tmp5, TEMP tmp6, KILL cr); |
| format %{ "String IndexOf $str1,$cnt1,$str2,$cnt2 -> $result (UL)" %} |
| |
| ins_encode %{ |
| __ string_indexof($str1$$Register, $str2$$Register, |
| $cnt1$$Register, $cnt2$$Register, |
| $tmp1$$Register, $tmp2$$Register, |
| $tmp3$$Register, $tmp4$$Register, |
| $tmp5$$Register, $tmp6$$Register, |
| $result$$Register, StrIntrinsicNode::UL); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_indexof_conUU(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, |
| immI_le_4 int_cnt2, iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, |
| iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) |
| %{ |
| predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU); |
| match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); |
| effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP_DEF result, |
| TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); |
| |
| format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (UU)" %} |
| |
| ins_encode %{ |
| int icnt2 = (int)$int_cnt2$$constant; |
| __ string_indexof_linearscan($str1$$Register, $str2$$Register, |
| $cnt1$$Register, zr, |
| $tmp1$$Register, $tmp2$$Register, |
| $tmp3$$Register, $tmp4$$Register, |
| icnt2, $result$$Register, StrIntrinsicNode::UU); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_indexof_conLL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, |
| immI_le_4 int_cnt2, iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, |
| iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) |
| %{ |
| predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL); |
| match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); |
| effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP_DEF result, |
| TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); |
| |
| format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (LL)" %} |
| ins_encode %{ |
| int icnt2 = (int)$int_cnt2$$constant; |
| __ string_indexof_linearscan($str1$$Register, $str2$$Register, |
| $cnt1$$Register, zr, |
| $tmp1$$Register, $tmp2$$Register, |
| $tmp3$$Register, $tmp4$$Register, |
| icnt2, $result$$Register, StrIntrinsicNode::LL); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_indexof_conUL(iRegP_R11 str1, iRegI_R12 cnt1, iRegP_R13 str2, |
| immI_1 int_cnt2, iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, |
| iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) |
| %{ |
| predicate(((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL); |
| match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2))); |
| effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt1, TEMP_DEF result, |
| TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); |
| |
| format %{ "String IndexOf $str1,$cnt1,$str2,$int_cnt2 -> $result (UL)" %} |
| ins_encode %{ |
| int icnt2 = (int)$int_cnt2$$constant; |
| __ string_indexof_linearscan($str1$$Register, $str2$$Register, |
| $cnt1$$Register, zr, |
| $tmp1$$Register, $tmp2$$Register, |
| $tmp3$$Register, $tmp4$$Register, |
| icnt2, $result$$Register, StrIntrinsicNode::UL); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct stringU_indexof_char(iRegP_R11 str1, iRegI_R12 cnt1, iRegI_R13 ch, |
| iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, |
| iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) |
| %{ |
| match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); |
| predicate(!UseRVV && (((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::U)); |
| effect(USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP_DEF result, |
| TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); |
| |
| format %{ "StringUTF16 IndexOf char[] $str1, $cnt1, $ch -> $result" %} |
| ins_encode %{ |
| __ string_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, |
| $result$$Register, $tmp1$$Register, $tmp2$$Register, |
| $tmp3$$Register, $tmp4$$Register, false /* isU */); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| |
| instruct stringL_indexof_char(iRegP_R11 str1, iRegI_R12 cnt1, iRegI_R13 ch, |
| iRegI_R10 result, iRegINoSp tmp1, iRegINoSp tmp2, |
| iRegINoSp tmp3, iRegINoSp tmp4, rFlagsReg cr) |
| %{ |
| match(Set result (StrIndexOfChar (Binary str1 cnt1) ch)); |
| predicate(!UseRVV && (((StrIndexOfCharNode*)n)->encoding() == StrIntrinsicNode::L)); |
| effect(USE_KILL str1, USE_KILL cnt1, USE_KILL ch, TEMP_DEF result, |
| TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL cr); |
| |
| format %{ "StringLatin1 IndexOf char[] $str1, $cnt1, $ch -> $result" %} |
| ins_encode %{ |
| __ string_indexof_char($str1$$Register, $cnt1$$Register, $ch$$Register, |
| $result$$Register, $tmp1$$Register, $tmp2$$Register, |
| $tmp3$$Register, $tmp4$$Register, true /* isL */); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| // clearing of an array |
| instruct clearArray_reg_reg(iRegL_R29 cnt, iRegP_R28 base, iRegP_R30 tmp1, |
| iRegP_R31 tmp2, Universe dummy) |
| %{ |
| // temp registers must match the one used in StubGenerator::generate_zero_blocks() |
| predicate(UseBlockZeroing || !UseRVV); |
| match(Set dummy (ClearArray cnt base)); |
| effect(USE_KILL cnt, USE_KILL base, TEMP tmp1, TEMP tmp2); |
| |
| ins_cost(4 * DEFAULT_COST); |
| format %{ "ClearArray $cnt, $base\t#@clearArray_reg_reg" %} |
| |
| ins_encode %{ |
| address tpc = __ zero_words($base$$Register, $cnt$$Register); |
| if (tpc == NULL) { |
| ciEnv::current()->record_failure("CodeCache is full"); |
| return; |
| } |
| %} |
| |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct clearArray_imm_reg(immL cnt, iRegP_R28 base, Universe dummy, rFlagsReg cr) |
| %{ |
| predicate(!UseRVV && (uint64_t)n->in(2)->get_long() |
| < (uint64_t)(BlockZeroingLowLimit >> LogBytesPerWord)); |
| match(Set dummy (ClearArray cnt base)); |
| effect(USE_KILL base, KILL cr); |
| |
| ins_cost(4 * DEFAULT_COST); |
| format %{ "ClearArray $cnt, $base\t#@clearArray_imm_reg" %} |
| |
| ins_encode %{ |
| __ zero_words($base$$Register, (uint64_t)$cnt$$constant); |
| %} |
| |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_equalsL(iRegP_R11 str1, iRegP_R13 str2, iRegI_R14 cnt, |
| iRegI_R10 result, rFlagsReg cr) |
| %{ |
| predicate(!UseRVV && ((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::LL); |
| match(Set result (StrEquals (Binary str1 str2) cnt)); |
| effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL cr); |
| |
| format %{ "String Equals $str1, $str2, $cnt -> $result\t#@string_equalsL" %} |
| ins_encode %{ |
| // Count is in 8-bit bytes; non-Compact chars are 16 bits. |
| __ string_equals($str1$$Register, $str2$$Register, |
| $result$$Register, $cnt$$Register, 1); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct string_equalsU(iRegP_R11 str1, iRegP_R13 str2, iRegI_R14 cnt, |
| iRegI_R10 result, rFlagsReg cr) |
| %{ |
| predicate(!UseRVV && ((StrEqualsNode*)n)->encoding() == StrIntrinsicNode::UU); |
| match(Set result (StrEquals (Binary str1 str2) cnt)); |
| effect(USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL cr); |
| |
| format %{ "String Equals $str1, $str2, $cnt -> $result\t#@string_equalsU" %} |
| ins_encode %{ |
| // Count is in 8-bit bytes; non-Compact chars are 16 bits. |
| __ string_equals($str1$$Register, $str2$$Register, |
| $result$$Register, $cnt$$Register, 2); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct array_equalsB(iRegP_R11 ary1, iRegP_R12 ary2, iRegI_R10 result, |
| iRegP_R13 tmp1, iRegP_R14 tmp2, iRegP_R15 tmp3, |
| iRegP_R16 tmp4, iRegP_R28 tmp5, rFlagsReg cr) |
| %{ |
| predicate(!UseRVV && ((AryEqNode*)n)->encoding() == StrIntrinsicNode::LL); |
| match(Set result (AryEq ary1 ary2)); |
| effect(USE_KILL ary1, USE_KILL ary2, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL tmp5, KILL cr); |
| |
| format %{ "Array Equals $ary1, ary2 -> $result\t#@array_equalsB // KILL $tmp5" %} |
| ins_encode %{ |
| __ arrays_equals($ary1$$Register, $ary2$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register, |
| $result$$Register, $tmp5$$Register, 1); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| instruct array_equalsC(iRegP_R11 ary1, iRegP_R12 ary2, iRegI_R10 result, |
| iRegP_R13 tmp1, iRegP_R14 tmp2, iRegP_R15 tmp3, |
| iRegP_R16 tmp4, iRegP_R28 tmp5, rFlagsReg cr) |
| %{ |
| predicate(!UseRVV && ((AryEqNode*)n)->encoding() == StrIntrinsicNode::UU); |
| match(Set result (AryEq ary1 ary2)); |
| effect(USE_KILL ary1, USE_KILL ary2, TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, KILL tmp5, KILL cr); |
| |
| format %{ "Array Equals $ary1, ary2 -> $result\t#@array_equalsC // KILL $tmp5" %} |
| ins_encode %{ |
| __ arrays_equals($ary1$$Register, $ary2$$Register, |
| $tmp1$$Register, $tmp2$$Register, $tmp3$$Register, $tmp4$$Register, |
| $result$$Register, $tmp5$$Register, 2); |
| %} |
| ins_pipe(pipe_class_memory); |
| %} |
| |
| // ============================================================================ |
| // Safepoint Instructions |
| |
| instruct safePoint(iRegP poll) |
| %{ |
| match(SafePoint poll); |
| |
| ins_cost(2 * LOAD_COST); |
| format %{ |
| "lwu zr, [$poll]\t# Safepoint: poll for GC, #@safePoint" |
| %} |
| ins_encode %{ |
| __ read_polling_page(as_Register($poll$$reg), 0, relocInfo::poll_type); |
| %} |
| ins_pipe(pipe_serial); // ins_pipe(iload_reg_mem); |
| %} |
| |
| // ============================================================================ |
| // This name is KNOWN by the ADLC and cannot be changed. |
| // The ADLC forces a 'TypeRawPtr::BOTTOM' output type |
| // for this guy. |
| instruct tlsLoadP(javaThread_RegP dst) |
| %{ |
| match(Set dst (ThreadLocal)); |
| |
| ins_cost(0); |
| |
| format %{ " -- \t// $dst=Thread::current(), empty, #@tlsLoadP" %} |
| |
| size(0); |
| |
| ins_encode( /*empty*/ ); |
| |
| ins_pipe(pipe_class_empty); |
| %} |
| |
| // inlined locking and unlocking |
| // using t1 as the 'flag' register to bridge the BoolNode producers and consumers |
| instruct cmpFastLock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2, iRegPNoSp tmp3) |
| %{ |
| match(Set cr (FastLock object box)); |
| effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); |
| |
| ins_cost(LOAD_COST * 2 + STORE_COST * 3 + ALU_COST * 6 + BRANCH_COST * 3); |
| format %{ "fastlock $object,$box\t! kills $tmp1,$tmp2,$tmp3, #@cmpFastLock" %} |
| |
| ins_encode %{ |
| __ fast_lock($object$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register, $tmp3$$Register); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| // using t1 as the 'flag' register to bridge the BoolNode producers and consumers |
| instruct cmpFastUnlock(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp tmp1, iRegPNoSp tmp2) |
| %{ |
| match(Set cr (FastUnlock object box)); |
| effect(TEMP tmp1, TEMP tmp2); |
| |
| ins_cost(LOAD_COST * 2 + STORE_COST + ALU_COST * 2 + BRANCH_COST * 4); |
| format %{ "fastunlock $object,$box\t! kills $tmp1, $tmp2, #@cmpFastUnlock" %} |
| |
| ins_encode %{ |
| __ fast_unlock($object$$Register, $box$$Register, $tmp1$$Register, $tmp2$$Register); |
| %} |
| |
| ins_pipe(pipe_serial); |
| %} |
| |
| // 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(iRegPNoSp jump_target, inline_cache_RegP method_oop) |
| %{ |
| match(TailCall jump_target method_oop); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "jalr $jump_target\t# $method_oop holds method oop, #@TailCalljmpInd." %} |
| |
| ins_encode(riscv_enc_tail_call(jump_target)); |
| |
| ins_pipe(pipe_class_call); |
| %} |
| |
| instruct TailjmpInd(iRegPNoSp jump_target, iRegP_R10 ex_oop) |
| %{ |
| match(TailJump jump_target ex_oop); |
| |
| ins_cost(ALU_COST + BRANCH_COST); |
| |
| format %{ "jalr $jump_target\t# $ex_oop holds exception oop, #@TailjmpInd." %} |
| |
| ins_encode(riscv_enc_tail_jmp(jump_target)); |
| |
| ins_pipe(pipe_class_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(iRegP_R10 ex_oop) |
| %{ |
| match(Set ex_oop (CreateEx)); |
| |
| ins_cost(0); |
| format %{ " -- \t// exception oop; no code emitted, #@CreateException" %} |
| |
| size(0); |
| |
| ins_encode( /*empty*/ ); |
| |
| ins_pipe(pipe_class_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(BRANCH_COST); |
| |
| format %{ "j rethrow_stub\t#@RethrowException" %} |
| |
| ins_encode(riscv_enc_rethrow()); |
| |
| ins_pipe(pipe_class_call); |
| %} |
| |
| // Return Instruction |
| // epilog node loads ret address into ra as part of frame pop |
| instruct Ret() |
| %{ |
| match(Return); |
| |
| ins_cost(BRANCH_COST); |
| format %{ "ret\t// return register, #@Ret" %} |
| |
| ins_encode(riscv_enc_ret()); |
| |
| ins_pipe(pipe_branch); |
| %} |
| |
| // Die now. |
| instruct ShouldNotReachHere() %{ |
| match(Halt); |
| |
| ins_cost(BRANCH_COST); |
| |
| format %{ "#@ShouldNotReachHere" %} |
| |
| ins_encode %{ |
| if (is_reachable()) { |
| __ stop(_halt_reason); |
| } |
| %} |
| |
| ins_pipe(pipe_class_default); |
| %} |
| |
| |
| //----------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 == RAX_enc) |
| // Only one replacement instruction |
| // |
| //----------SMARTSPILL RULES--------------------------------------------------- |
| // These must follow all instruction definitions as they use the names |
| // defined in the instructions definitions. |
| |
| // Local Variables: |
| // mode: c++ |
| // End: |