void FpuStackAllocator::handle_op2(LIR_Op2* op2) { LIR_Opr left = op2->in_opr1(); if (!left->is_float_kind()) { return; } if (left->is_xmm_register()) { return; } LIR_Opr right = op2->in_opr2(); LIR_Opr res = op2->result_opr(); LIR_Opr new_left = left; // new operands relative to the actual fpu stack top LIR_Opr new_right = right; LIR_Opr new_res = res; assert(!left->is_xmm_register() && !right->is_xmm_register() && !res->is_xmm_register(), "not for xmm registers"); switch (op2->code()) { case lir_cmp: case lir_cmp_fd2i: case lir_ucmp_fd2i: case lir_assert: { assert(left->is_fpu_register(), "invalid LIR"); assert(right->is_fpu_register(), "invalid LIR"); // the left-hand side must be on top of stack. // the right-hand side is never popped, even if is_last_use is set insert_exchange(left); new_left = to_fpu_stack_top(left); new_right = to_fpu_stack(right); pop_if_last_use(op2, left); break; } case lir_mul_strictfp: case lir_div_strictfp: { assert(op2->tmp1_opr()->is_fpu_register(), "strict operations need temporary fpu stack slot"); insert_free_if_dead(op2->tmp1_opr()); assert(sim()->stack_size() <= 7, "at least one stack slot must be free"); // fall-through: continue with the normal handling of lir_mul and lir_div } case lir_add: case lir_sub: case lir_mul: case lir_div: { assert(left->is_fpu_register(), "must be"); assert(res->is_fpu_register(), "must be"); assert(left->is_equal(res), "must be"); // either the left-hand or the right-hand side must be on top of stack // (if right is not a register, left must be on top) if (!right->is_fpu_register()) { insert_exchange(left); new_left = to_fpu_stack_top(left); } else { // no exchange necessary if right is alredy on top of stack if (tos_offset(right) == 0) { new_left = to_fpu_stack(left); new_right = to_fpu_stack_top(right); } else { insert_exchange(left); new_left = to_fpu_stack_top(left); new_right = to_fpu_stack(right); } if (right->is_last_use()) { op2->set_fpu_pop_count(1); if (tos_offset(right) == 0) { sim()->pop(); } else { // if left is on top of stack, the result is placed in the stack // slot of right, so a renaming from right to res is necessary assert(tos_offset(left) == 0, "must be"); sim()->pop(); do_rename(right, res); } } } new_res = to_fpu_stack(res); break; } case lir_rem: { assert(left->is_fpu_register(), "must be"); assert(right->is_fpu_register(), "must be"); assert(res->is_fpu_register(), "must be"); assert(left->is_equal(res), "must be"); // Must bring both operands to top of stack with following operand ordering: // * fpu stack before rem: ... right left // * fpu stack after rem: ... left if (tos_offset(right) != 1) { insert_exchange(right); insert_exchange(1); } insert_exchange(left); assert(tos_offset(right) == 1, "check"); assert(tos_offset(left) == 0, "check"); new_left = to_fpu_stack_top(left); new_right = to_fpu_stack(right); op2->set_fpu_pop_count(1); sim()->pop(); do_rename(right, res); new_res = to_fpu_stack_top(res); break; } case lir_abs: case lir_sqrt: { // Right argument appears to be unused assert(right->is_illegal(), "must be"); assert(left->is_fpu_register(), "must be"); assert(res->is_fpu_register(), "must be"); assert(left->is_last_use(), "old value gets destroyed"); insert_free_if_dead(res, left); insert_exchange(left); do_rename(left, res); new_left = to_fpu_stack_top(res); new_res = new_left; op2->set_fpu_stack_size(sim()->stack_size()); break; } default: { assert(false, "missed a fpu-operation"); } } op2->set_in_opr1(new_left); op2->set_in_opr2(new_right); op2->set_result_opr(new_res); }
void FpuStackAllocator::handle_op1(LIR_Op1* op1) { LIR_Opr in = op1->in_opr(); LIR_Opr res = op1->result_opr(); LIR_Opr new_in = in; // new operands relative to the actual fpu stack top LIR_Opr new_res = res; // Note: this switch is processed for all LIR_Op1, regardless if they have FPU-arguments, // so checks for is_float_kind() are necessary inside the cases switch (op1->code()) { case lir_return: { // FPU-Stack must only contain the (optional) fpu return value. // All remaining dead values are popped from the stack // If the input operand is a fpu-register, it is exchanged to the bottom of the stack clear_fpu_stack(in); if (in->is_fpu_register() && !in->is_xmm_register()) { new_in = to_fpu_stack_top(in); } break; } case lir_move: { if (in->is_fpu_register() && !in->is_xmm_register()) { if (res->is_xmm_register()) { // move from fpu register to xmm register (necessary for operations that // are not available in the SSE instruction set) insert_exchange(in); new_in = to_fpu_stack_top(in); pop_always(op1, in); } else if (res->is_fpu_register() && !res->is_xmm_register()) { // move from fpu-register to fpu-register: // * input and result register equal: // nothing to do // * input register is last use: // rename the input register to result register -> input register // not present on fpu-stack afterwards // * input register not last use: // duplicate input register to result register to preserve input // // Note: The LIR-Assembler does not produce any code for fpu register moves, // so input and result stack index must be equal if (fpu_num(in) == fpu_num(res)) { // nothing to do } else if (in->is_last_use()) { insert_free_if_dead(res);//, in); do_rename(in, res); } else { insert_free_if_dead(res); insert_copy(in, res); } new_in = to_fpu_stack(res); new_res = new_in; } else { // move from fpu-register to memory // input operand must be on top of stack insert_exchange(in); // create debug information here because afterwards the register may have been popped compute_debug_information(op1); new_in = to_fpu_stack_top(in); pop_if_last_use(op1, in); } } else if (res->is_fpu_register() && !res->is_xmm_register()) { // move from memory/constant to fpu register // result is pushed on the stack insert_free_if_dead(res); // create debug information before register is pushed compute_debug_information(op1); do_push(res); new_res = to_fpu_stack_top(res); } break; } case lir_neg: { if (in->is_fpu_register() && !in->is_xmm_register()) { assert(res->is_fpu_register() && !res->is_xmm_register(), "must be"); assert(in->is_last_use(), "old value gets destroyed"); insert_free_if_dead(res, in); insert_exchange(in); new_in = to_fpu_stack_top(in); do_rename(in, res); new_res = to_fpu_stack_top(res); } break; } case lir_convert: { Bytecodes::Code bc = op1->as_OpConvert()->bytecode(); switch (bc) { case Bytecodes::_d2f: case Bytecodes::_f2d: assert(res->is_fpu_register(), "must be"); assert(in->is_fpu_register(), "must be"); if (!in->is_xmm_register() && !res->is_xmm_register()) { // this is quite the same as a move from fpu-register to fpu-register // Note: input and result operands must have different types if (fpu_num(in) == fpu_num(res)) { // nothing to do new_in = to_fpu_stack(in); } else if (in->is_last_use()) { insert_free_if_dead(res);//, in); new_in = to_fpu_stack(in); do_rename(in, res); } else { insert_free_if_dead(res); insert_copy(in, res); new_in = to_fpu_stack_top(in, true); } new_res = to_fpu_stack(res); } break; case Bytecodes::_i2f: case Bytecodes::_l2f: case Bytecodes::_i2d: case Bytecodes::_l2d: assert(res->is_fpu_register(), "must be"); if (!res->is_xmm_register()) { insert_free_if_dead(res); do_push(res); new_res = to_fpu_stack_top(res); } break; case Bytecodes::_f2i: case Bytecodes::_d2i: assert(in->is_fpu_register(), "must be"); if (!in->is_xmm_register()) { insert_exchange(in); new_in = to_fpu_stack_top(in); // TODO: update registes of stub } break; case Bytecodes::_f2l: case Bytecodes::_d2l: assert(in->is_fpu_register(), "must be"); if (!in->is_xmm_register()) { insert_exchange(in); new_in = to_fpu_stack_top(in); pop_always(op1, in); } break; case Bytecodes::_i2l: case Bytecodes::_l2i: case Bytecodes::_i2b: case Bytecodes::_i2c: case Bytecodes::_i2s: // no fpu operands break; default: ShouldNotReachHere(); } break; } case lir_roundfp: { assert(in->is_fpu_register() && !in->is_xmm_register(), "input must be in register"); assert(res->is_stack(), "result must be on stack"); insert_exchange(in); new_in = to_fpu_stack_top(in); pop_if_last_use(op1, in); break; } default: { assert(!in->is_float_kind() && !res->is_float_kind(), "missed a fpu-operation"); } } op1->set_in_opr(new_in); op1->set_result_opr(new_res); }