void FpuStackAllocator::insert_copy(LIR_Opr from, LIR_Opr to) { int offset = tos_offset(from); LIR_Op1* fld = new LIR_Op1(lir_fld, LIR_OprFact::intConst(offset), LIR_OprFact::illegalOpr); insert_op(fld); sim()->push(fpu_num(to)); #ifndef PRODUCT if (TraceFPUStack) { tty->print("Inserted copy (%d -> %d) New state: ", fpu_num(from), fpu_num(to)); sim()->print(); tty->cr(); } #endif }
void FpuStackAllocator::clear_fpu_stack(LIR_Opr preserve) { int result_stack_size = (preserve->is_fpu_register() && !preserve->is_xmm_register() ? 1 : 0); while (sim()->stack_size() > result_stack_size) { assert(!sim()->slot_is_empty(0), "not allowed"); if (result_stack_size == 0 || sim()->get_slot(0) != fpu_num(preserve)) { insert_free(0); } else { // move "preserve" to bottom of stack so that all other stack slots can be popped insert_exchange(sim()->stack_size() - 1); } } }
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); }
void FpuStackAllocator::do_push(LIR_Opr opr) { sim()->push(fpu_num(opr)); }
void FpuStackAllocator::do_rename(LIR_Opr from, LIR_Opr to) { sim()->rename(fpu_num(from), fpu_num(to)); }
void FpuStackAllocator::insert_free_if_dead(LIR_Opr opr, LIR_Opr ignore) { if (fpu_num(opr) != fpu_num(ignore) && sim()->contains(fpu_num(opr))) { int res_slot = tos_offset(opr); insert_free(res_slot); } }
int FpuStackAllocator::tos_offset(LIR_Opr opr) { return sim()->offset_from_tos(fpu_num(opr)); }
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: { 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->tmp_opr()->is_fpu_register(), "strict operations need temporary fpu stack slot"); insert_free_if_dead(op2->tmp_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; } case lir_log: case lir_log10: { // log and log10 needs one temporary fpu stack slot, so there is ontemporary // registers stored in temp of the operation. // the stack allocator must guarantee that the stack slots are really free, // otherwise there might be a stack overflow. assert(right->is_illegal(), "must be"); assert(left->is_fpu_register(), "must be"); assert(res->is_fpu_register(), "must be"); assert(op2->tmp_opr()->is_fpu_register(), "must be"); insert_free_if_dead(op2->tmp_opr()); 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()); assert(sim()->stack_size() <= 7, "at least one stack slot must be free"); break; } case lir_tan: case lir_sin: case lir_cos: { // sin and cos need two temporary fpu stack slots, so there are two temporary // registers (stored in right and temp of the operation). // the stack allocator must guarantee that the stack slots are really free, // otherwise there might be a stack overflow. assert(left->is_fpu_register(), "must be"); assert(res->is_fpu_register(), "must be"); // assert(left->is_last_use(), "old value gets destroyed"); assert(right->is_fpu_register(), "right is used as the first temporary register"); assert(op2->tmp_opr()->is_fpu_register(), "temp is used as the second temporary register"); assert(fpu_num(left) != fpu_num(right) && fpu_num(right) != fpu_num(op2->tmp_opr()) && fpu_num(op2->tmp_opr()) != fpu_num(res), "need distinct temp registers"); insert_free_if_dead(right); insert_free_if_dead(op2->tmp_opr()); 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()); assert(sim()->stack_size() <= 6, "at least two stack slots must be free"); 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); }