blob: 787868d4c38f1c627b7d36b3c10ee2020c574fd3 [file]
/*
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2022, 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.
*
*/
#include "asm/assembler.hpp"
#include "c1/c1_LIRAssembler.hpp"
#include "c1/c1_MacroAssembler.hpp"
#ifndef PRODUCT
#define COMMENT(x) do { __ block_comment(x); } while (0)
#else
#define COMMENT(x)
#endif
#define __ _masm->
void LIR_Assembler::arithmetic_idiv(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr illegal,
LIR_Opr result, CodeEmitInfo* info) {
// opcode check
assert((code == lir_idiv) || (code == lir_irem), "opcode must be idiv or irem");
bool is_irem = (code == lir_irem);
// opreand check
assert(left->is_single_cpu(), "left must be a register");
assert(right->is_single_cpu() || right->is_constant(), "right must be a register or constant");
assert(result->is_single_cpu(), "result must be a register");
Register lreg = left->as_register();
Register dreg = result->as_register();
// power-of-2 constant check and codegen
if (right->is_constant()) {
int c = right->as_constant_ptr()->as_jint();
assert(c > 0 && is_power_of_2(c), "divisor must be power-of-2 constant");
if (is_irem) {
if (c == 1) {
// move 0 to dreg if divisor is 1
__ mv(dreg, zr);
} else {
unsigned int shift = exact_log2(c);
__ sraiw(t0, lreg, 0x1f);
__ srliw(t0, t0, BitsPerInt - shift);
__ addw(t1, lreg, t0);
if (Assembler::is_simm12(c - 1)) {
__ andi(t1, t1, c - 1);
} else {
__ zext(t1, t1, shift);
}
__ subw(dreg, t1, t0);
}
} else {
if (c == 1) {
// move lreg to dreg if divisor is 1
__ mv(dreg, lreg);
} else {
unsigned int shift = exact_log2(c);
__ sraiw(t0, lreg, 0x1f);
if (Assembler::is_simm12(c - 1)) {
__ andi(t0, t0, c - 1);
} else {
__ zext(t0, t0, shift);
}
__ addw(dreg, t0, lreg);
__ sraiw(dreg, dreg, shift);
}
}
} else {
Register rreg = right->as_register();
if (is_irem) {
__ remw(dreg, lreg, rreg);
} else {
__ divw(dreg, lreg, rreg);
}
}
}
void LIR_Assembler::arith_op_single_cpu_right_constant(LIR_Code code, LIR_Opr left, LIR_Opr right,
Register lreg, Register dreg) {
// cpu register - constant
jlong c;
switch (right->type()) {
case T_LONG:
c = right->as_constant_ptr()->as_jlong(); break;
case T_INT: // fall through
case T_ADDRESS:
c = right->as_constant_ptr()->as_jint(); break;
default:
ShouldNotReachHere();
c = 0; // unreachable
}
assert(code == lir_add || code == lir_sub, "mismatched arithmetic op");
if (c == 0 && dreg == lreg) {
COMMENT("effective nop elided");
return;
}
switch (left->type()) {
case T_INT:
switch (code) {
case lir_add: __ addw(dreg, lreg, c); break;
case lir_sub: __ subw(dreg, lreg, c); break;
default: ShouldNotReachHere();
}
break;
case T_OBJECT: // fall through
case T_ADDRESS:
switch (code) {
case lir_add: __ add(dreg, lreg, c); break;
case lir_sub: __ sub(dreg, lreg, c); break;
default: ShouldNotReachHere();
}
break;
default:
ShouldNotReachHere();
}
}
void LIR_Assembler::arith_op_single_cpu(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest) {
Register lreg = left->as_register();
Register dreg = as_reg(dest);
if (right->is_single_cpu()) {
// cpu register - cpu register
assert(left->type() == T_INT && right->type() == T_INT && dest->type() == T_INT, "should be");
Register rreg = right->as_register();
switch (code) {
case lir_add: __ addw(dest->as_register(), lreg, rreg); break;
case lir_sub: __ subw(dest->as_register(), lreg, rreg); break;
case lir_mul: __ mulw(dest->as_register(), lreg, rreg); break;
default: ShouldNotReachHere();
}
} else if (right->is_double_cpu()) {
Register rreg = right->as_register_lo();
// sigle_cpu + double_cpu; can happen with obj_long
assert(code == lir_add || code == lir_sub, "mismatched arithmetic op");
switch (code) {
case lir_add: __ add(dreg, lreg, rreg); break;
case lir_sub: __ sub(dreg, lreg, rreg); break;
default: ShouldNotReachHere();
}
} else if (right->is_constant()) {
arith_op_single_cpu_right_constant(code, left, right, lreg, dreg);
} else {
ShouldNotReachHere();
}
}
void LIR_Assembler::arith_op_double_cpu(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest) {
Register lreg_lo = left->as_register_lo();
if (right->is_double_cpu()) {
// cpu register - cpu register
Register rreg_lo = right->as_register_lo();
switch (code) {
case lir_add: __ add(dest->as_register_lo(), lreg_lo, rreg_lo); break;
case lir_sub: __ sub(dest->as_register_lo(), lreg_lo, rreg_lo); break;
case lir_mul: __ mul(dest->as_register_lo(), lreg_lo, rreg_lo); break;
case lir_div: __ div(dest->as_register_lo(), lreg_lo, rreg_lo); break;
case lir_rem: __ rem(dest->as_register_lo(), lreg_lo, rreg_lo); break;
default:
ShouldNotReachHere();
}
} else if (right->is_constant()) {
jlong c = right->as_constant_ptr()->as_jlong();
Register dreg = as_reg(dest);
switch (code) {
case lir_add: // fall through
case lir_sub:
if (c == 0 && dreg == lreg_lo) {
COMMENT("effective nop elided");
return;
}
code == lir_add ? __ add(dreg, lreg_lo, c) : __ sub(dreg, lreg_lo, c);
break;
case lir_div:
assert(c > 0 && is_power_of_2(c), "divisor must be power-of-2 constant");
if (c == 1) {
// move lreg_lo to dreg if divisor is 1
__ mv(dreg, lreg_lo);
} else {
unsigned int shift = exact_log2_long(c);
// use t0 as intermediate result register
__ srai(t0, lreg_lo, 0x3f);
if (Assembler::is_simm12(c - 1)) {
__ andi(t0, t0, c - 1);
} else {
__ zext(t0, t0, shift);
}
__ add(dreg, t0, lreg_lo);
__ srai(dreg, dreg, shift);
}
break;
case lir_rem:
assert(c > 0 && is_power_of_2(c), "divisor must be power-of-2 constant");
if (c == 1) {
// move 0 to dreg if divisor is 1
__ mv(dreg, zr);
} else {
unsigned int shift = exact_log2_long(c);
__ srai(t0, lreg_lo, 0x3f);
__ srli(t0, t0, BitsPerLong - shift);
__ add(t1, lreg_lo, t0);
if (Assembler::is_simm12(c - 1)) {
__ andi(t1, t1, c - 1);
} else {
__ zext(t1, t1, shift);
}
__ sub(dreg, t1, t0);
}
break;
default:
ShouldNotReachHere();
}
} else {
ShouldNotReachHere();
}
}
void LIR_Assembler::arith_op_single_fpu(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest) {
assert(right->is_single_fpu(), "right hand side of float arithmetics needs to be float register");
switch (code) {
case lir_add: __ fadd_s(dest->as_float_reg(), left->as_float_reg(), right->as_float_reg()); break;
case lir_sub: __ fsub_s(dest->as_float_reg(), left->as_float_reg(), right->as_float_reg()); break;
case lir_mul: __ fmul_s(dest->as_float_reg(), left->as_float_reg(), right->as_float_reg()); break;
case lir_div: __ fdiv_s(dest->as_float_reg(), left->as_float_reg(), right->as_float_reg()); break;
default:
ShouldNotReachHere();
}
}
void LIR_Assembler::arith_op_double_fpu(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest) {
if (right->is_double_fpu()) {
// fpu register - fpu register
switch (code) {
case lir_add: __ fadd_d(dest->as_double_reg(), left->as_double_reg(), right->as_double_reg()); break;
case lir_sub: __ fsub_d(dest->as_double_reg(), left->as_double_reg(), right->as_double_reg()); break;
case lir_mul: __ fmul_d(dest->as_double_reg(), left->as_double_reg(), right->as_double_reg()); break;
case lir_div: __ fdiv_d(dest->as_double_reg(), left->as_double_reg(), right->as_double_reg()); break;
default:
ShouldNotReachHere();
}
} else {
ShouldNotReachHere();
}
}
void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest,
CodeEmitInfo* info) {
assert(info == nullptr, "should never be used, idiv/irem and ldiv/lrem not handled by this method");
if (left->is_single_cpu()) {
arith_op_single_cpu(code, left, right, dest);
} else if (left->is_double_cpu()) {
arith_op_double_cpu(code, left, right, dest);
} else if (left->is_single_fpu()) {
arith_op_single_fpu(code, left, right, dest);
} else if (left->is_double_fpu()) {
arith_op_double_fpu(code, left, right, dest);
} else {
ShouldNotReachHere();
}
}
#undef __