/*
 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2012, 2014 SAP AG. 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 "precompiled.hpp"
#include "asm/assembler.inline.hpp"
#include "gc_interface/collectedHeap.inline.hpp"
#include "interpreter/interpreter.hpp"
#include "memory/cardTableModRefBS.hpp"
#include "memory/resourceArea.hpp"
#include "prims/methodHandles.hpp"
#include "runtime/biasedLocking.hpp"
#include "runtime/interfaceSupport.hpp"
#include "runtime/objectMonitor.hpp"
#include "runtime/os.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
#include "utilities/macros.hpp"
#if INCLUDE_ALL_GCS
#include "gc_implementation/g1/g1CollectedHeap.inline.hpp"
#include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp"
#include "gc_implementation/g1/heapRegion.hpp"
#endif // INCLUDE_ALL_GCS

#ifdef PRODUCT
#define BLOCK_COMMENT(str) // nothing
#else
#define BLOCK_COMMENT(str) block_comment(str)
#endif

int AbstractAssembler::code_fill_byte() {
  return 0x00;                  // illegal instruction 0x00000000
}

void Assembler::print_instruction(int inst) {
  Unimplemented();
}

// Patch instruction `inst' at offset `inst_pos' to refer to
// `dest_pos' and return the resulting instruction.  We should have
// pcs, not offsets, but since all is relative, it will work out fine.
int Assembler::patched_branch(int dest_pos, int inst, int inst_pos) {
  int m = 0; // mask for displacement field
  int v = 0; // new value for displacement field

  switch (inv_op_ppc(inst)) {
  case b_op:  m = li(-1); v = li(disp(dest_pos, inst_pos)); break;
  case bc_op: m = bd(-1); v = bd(disp(dest_pos, inst_pos)); break;
    default: ShouldNotReachHere();
  }
  return inst & ~m | v;
}

// Return the offset, relative to _code_begin, of the destination of
// the branch inst at offset pos.
int Assembler::branch_destination(int inst, int pos) {
  int r = 0;
  switch (inv_op_ppc(inst)) {
    case b_op:  r = bxx_destination_offset(inst, pos); break;
    case bc_op: r = inv_bd_field(inst, pos); break;
    default: ShouldNotReachHere();
  }
  return r;
}

// Low-level andi-one-instruction-macro.
void Assembler::andi(Register a, Register s, const int ui16) {
  assert(is_uimm(ui16, 16), "must be 16-bit unsigned immediate");
  if (is_power_of_2_long(((jlong) ui16)+1)) {
    // pow2minus1
    clrldi(a, s, 64-log2_long((((jlong) ui16)+1)));
  } else if (is_power_of_2_long((jlong) ui16)) {
    // pow2
    rlwinm(a, s, 0, 31-log2_long((jlong) ui16), 31-log2_long((jlong) ui16));
  } else if (is_power_of_2_long((jlong)-ui16)) {
    // negpow2
    clrrdi(a, s, log2_long((jlong)-ui16));
  } else {
    andi_(a, s, ui16);
  }
}

// RegisterOrConstant version.
void Assembler::ld(Register d, RegisterOrConstant roc, Register s1) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true);
      Assembler::ld(d, simm16_rest, d);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::ld(d, roc.as_constant(), s1);
    } else {
      load_const_optimized(d, roc.as_constant());
      Assembler::ldx(d, d, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::ld(d, 0, roc.as_register());
    else
      Assembler::ldx(d, roc.as_register(), s1);
  }
}

void Assembler::lwa(Register d, RegisterOrConstant roc, Register s1) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true);
      Assembler::lwa(d, simm16_rest, d);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::lwa(d, roc.as_constant(), s1);
    } else {
      load_const_optimized(d, roc.as_constant());
      Assembler::lwax(d, d, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::lwa(d, 0, roc.as_register());
    else
      Assembler::lwax(d, roc.as_register(), s1);
  }
}

void Assembler::lwz(Register d, RegisterOrConstant roc, Register s1) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true);
      Assembler::lwz(d, simm16_rest, d);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::lwz(d, roc.as_constant(), s1);
    } else {
      load_const_optimized(d, roc.as_constant());
      Assembler::lwzx(d, d, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::lwz(d, 0, roc.as_register());
    else
      Assembler::lwzx(d, roc.as_register(), s1);
  }
}

void Assembler::lha(Register d, RegisterOrConstant roc, Register s1) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true);
      Assembler::lha(d, simm16_rest, d);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::lha(d, roc.as_constant(), s1);
    } else {
      load_const_optimized(d, roc.as_constant());
      Assembler::lhax(d, d, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::lha(d, 0, roc.as_register());
    else
      Assembler::lhax(d, roc.as_register(), s1);
  }
}

void Assembler::lhz(Register d, RegisterOrConstant roc, Register s1) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true);
      Assembler::lhz(d, simm16_rest, d);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::lhz(d, roc.as_constant(), s1);
    } else {
      load_const_optimized(d, roc.as_constant());
      Assembler::lhzx(d, d, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::lhz(d, 0, roc.as_register());
    else
      Assembler::lhzx(d, roc.as_register(), s1);
  }
}

void Assembler::lbz(Register d, RegisterOrConstant roc, Register s1) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      int simm16_rest = load_const_optimized(d, roc.as_constant(), noreg, true);
      Assembler::lbz(d, simm16_rest, d);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::lbz(d, roc.as_constant(), s1);
    } else {
      load_const_optimized(d, roc.as_constant());
      Assembler::lbzx(d, d, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::lbz(d, 0, roc.as_register());
    else
      Assembler::lbzx(d, roc.as_register(), s1);
  }
}

void Assembler::std(Register d, RegisterOrConstant roc, Register s1, Register tmp) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      guarantee(tmp != noreg, "Need tmp reg to encode large constants");
      int simm16_rest = load_const_optimized(tmp, roc.as_constant(), noreg, true);
      Assembler::std(d, simm16_rest, tmp);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::std(d, roc.as_constant(), s1);
    } else {
      guarantee(tmp != noreg, "Need tmp reg to encode large constants");
      load_const_optimized(tmp, roc.as_constant());
      Assembler::stdx(d, tmp, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::std(d, 0, roc.as_register());
    else
      Assembler::stdx(d, roc.as_register(), s1);
  }
}

void Assembler::stw(Register d, RegisterOrConstant roc, Register s1, Register tmp) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      guarantee(tmp != noreg, "Need tmp reg to encode large constants");
      int simm16_rest = load_const_optimized(tmp, roc.as_constant(), noreg, true);
      Assembler::stw(d, simm16_rest, tmp);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::stw(d, roc.as_constant(), s1);
    } else {
      guarantee(tmp != noreg, "Need tmp reg to encode large constants");
      load_const_optimized(tmp, roc.as_constant());
      Assembler::stwx(d, tmp, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::stw(d, 0, roc.as_register());
    else
      Assembler::stwx(d, roc.as_register(), s1);
  }
}

void Assembler::sth(Register d, RegisterOrConstant roc, Register s1, Register tmp) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      guarantee(tmp != noreg, "Need tmp reg to encode large constants");
      int simm16_rest = load_const_optimized(tmp, roc.as_constant(), noreg, true);
      Assembler::sth(d, simm16_rest, tmp);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::sth(d, roc.as_constant(), s1);
    } else {
      guarantee(tmp != noreg, "Need tmp reg to encode large constants");
      load_const_optimized(tmp, roc.as_constant());
      Assembler::sthx(d, tmp, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::sth(d, 0, roc.as_register());
    else
      Assembler::sthx(d, roc.as_register(), s1);
  }
}

void Assembler::stb(Register d, RegisterOrConstant roc, Register s1, Register tmp) {
  if (roc.is_constant()) {
    if (s1 == noreg) {
      guarantee(tmp != noreg, "Need tmp reg to encode large constants");
      int simm16_rest = load_const_optimized(tmp, roc.as_constant(), noreg, true);
      Assembler::stb(d, simm16_rest, tmp);
    } else if (is_simm(roc.as_constant(), 16)) {
      Assembler::stb(d, roc.as_constant(), s1);
    } else {
      guarantee(tmp != noreg, "Need tmp reg to encode large constants");
      load_const_optimized(tmp, roc.as_constant());
      Assembler::stbx(d, tmp, s1);
    }
  } else {
    if (s1 == noreg)
      Assembler::stb(d, 0, roc.as_register());
    else
      Assembler::stbx(d, roc.as_register(), s1);
  }
}

void Assembler::add(Register d, RegisterOrConstant roc, Register s1) {
  if (roc.is_constant()) {
    intptr_t c = roc.as_constant();
    assert(is_simm(c, 16), "too big");
    addi(d, s1, (int)c);
  }
  else add(d, roc.as_register(), s1);
}

void Assembler::subf(Register d, RegisterOrConstant roc, Register s1) {
  if (roc.is_constant()) {
    intptr_t c = roc.as_constant();
    assert(is_simm(-c, 16), "too big");
    addi(d, s1, (int)-c);
  }
  else subf(d, roc.as_register(), s1);
}

void Assembler::cmpd(ConditionRegister d, RegisterOrConstant roc, Register s1) {
  if (roc.is_constant()) {
    intptr_t c = roc.as_constant();
    assert(is_simm(c, 16), "too big");
    cmpdi(d, s1, (int)c);
  }
  else cmpd(d, roc.as_register(), s1);
}

// Load a 64 bit constant. Patchable.
void Assembler::load_const(Register d, long x, Register tmp) {
  // 64-bit value: x = xa xb xc xd
  int xa = (x >> 48) & 0xffff;
  int xb = (x >> 32) & 0xffff;
  int xc = (x >> 16) & 0xffff;
  int xd = (x >>  0) & 0xffff;
  if (tmp == noreg) {
    Assembler::lis( d, (int)(short)xa);
    Assembler::ori( d, d, (unsigned int)xb);
    Assembler::sldi(d, d, 32);
    Assembler::oris(d, d, (unsigned int)xc);
    Assembler::ori( d, d, (unsigned int)xd);
  } else {
    // exploit instruction level parallelism if we have a tmp register
    assert_different_registers(d, tmp);
    Assembler::lis(tmp, (int)(short)xa);
    Assembler::lis(d, (int)(short)xc);
    Assembler::ori(tmp, tmp, (unsigned int)xb);
    Assembler::ori(d, d, (unsigned int)xd);
    Assembler::insrdi(d, tmp, 32, 0);
  }
}

// Load a 64 bit constant, optimized, not identifyable.
// Tmp can be used to increase ILP. Set return_simm16_rest=true to get a
// 16 bit immediate offset.
int Assembler::load_const_optimized(Register d, long x, Register tmp, bool return_simm16_rest) {
  // Avoid accidentally trying to use R0 for indexed addressing.
  assert(d != R0, "R0 not allowed");
  assert_different_registers(d, tmp);

  short xa, xb, xc, xd; // Four 16-bit chunks of const.
  long rem = x;         // Remaining part of const.

  xd = rem & 0xFFFF;    // Lowest 16-bit chunk.
  rem = (rem >> 16) + ((unsigned short)xd >> 15); // Compensation for sign extend.

  if (rem == 0) { // opt 1: simm16
    li(d, xd);
    return 0;
  }

  xc = rem & 0xFFFF; // Next 16-bit chunk.
  rem = (rem >> 16) + ((unsigned short)xc >> 15); // Compensation for sign extend.

  if (rem == 0) { // opt 2: simm32
    lis(d, xc);
  } else { // High 32 bits needed.

    if (tmp != noreg) { // opt 3: We have a temp reg.
      // No carry propagation between xc and higher chunks here (use logical instructions).
      xa = (x >> 48) & 0xffff;
      xb = (x >> 32) & 0xffff; // No sign compensation, we use lis+ori or li to allow usage of R0.
      bool load_xa = (xa != 0) || (xb < 0);
      bool return_xd = false;

      if (load_xa) { lis(tmp, xa); }
      if (xc) { lis(d, xc); }
      if (load_xa) {
        if (xb) { ori(tmp, tmp, (unsigned short)xb); } // No addi, we support tmp == R0.
      } else {
        li(tmp, xb); // non-negative
      }
      if (xc) {
        if (return_simm16_rest && xd >= 0) { return_xd = true; } // >= 0 to avoid carry propagation after insrdi/rldimi.
        else if (xd) { addi(d, d, xd); }
      } else {
        li(d, xd);
      }
      insrdi(d, tmp, 32, 0);
      return return_xd ? xd : 0; // non-negative
    }

    xb = rem & 0xFFFF; // Next 16-bit chunk.
    rem = (rem >> 16) + ((unsigned short)xb >> 15); // Compensation for sign extend.

    xa = rem & 0xFFFF; // Highest 16-bit chunk.

    // opt 4: avoid adding 0
    if (xa) { // Highest 16-bit needed?
      lis(d, xa);
      if (xb) { addi(d, d, xb); }
    } else {
      li(d, xb);
    }
    sldi(d, d, 32);
    if (xc) { addis(d, d, xc); }
  }

  // opt 5: Return offset to be inserted into following instruction.
  if (return_simm16_rest) return xd;

  if (xd) { addi(d, d, xd); }
  return 0;
}

#ifndef PRODUCT
// Test of ppc assembler.
void Assembler::test_asm() {
  // PPC 1, section 3.3.8, Fixed-Point Arithmetic Instructions
  addi(   R0,  R1,  10);
  addis(  R5,  R2,  11);
  addic_( R3,  R31, 42);
  subfic( R21, R12, 2112);
  add(    R3,  R2,  R1);
  add_(   R11, R22, R30);
  subf(   R7,  R6,  R5);
  subf_(  R8,  R9,  R4);
  addc(   R11, R12, R13);
  addc_(  R14, R14, R14);
  subfc(  R15, R16, R17);
  subfc_( R18, R20, R19);
  adde(   R20, R22, R24);
  adde_(  R29, R27, R26);
  subfe(  R28, R1,  R0);
  subfe_( R21, R11, R29);
  neg(    R21, R22);
  neg_(   R13, R23);
  mulli(  R0,  R11, -31);
  mulld(  R1,  R18, R21);
  mulld_( R2,  R17, R22);
  mullw(  R3,  R16, R23);
  mullw_( R4,  R15, R24);
  divd(   R5,  R14, R25);
  divd_(  R6,  R13, R26);
  divw(   R7,  R12, R27);
  divw_(  R8,  R11, R28);

  li(     R3, -4711);

  // PPC 1, section 3.3.9, Fixed-Point Compare Instructions
  cmpi(   CCR7,  0, R27, 4711);
  cmp(    CCR0, 1, R14, R11);
  cmpli(  CCR5,  1, R17, 45);
  cmpl(   CCR3, 0, R9,  R10);

  cmpwi(  CCR7,  R27, 4711);
  cmpw(   CCR0, R14, R11);
  cmplwi( CCR5,  R17, 45);
  cmplw(  CCR3, R9,  R10);

  cmpdi(  CCR7,  R27, 4711);
  cmpd(   CCR0, R14, R11);
  cmpldi( CCR5,  R17, 45);
  cmpld(  CCR3, R9,  R10);

  // PPC 1, section 3.3.11, Fixed-Point Logical Instructions
  andi_(  R4,  R5,  0xff);
  andis_( R12, R13, 0x7b51);
  ori(    R1,  R4,  13);
  oris(   R3,  R5,  177);
  xori(   R7,  R6,  51);
  xoris(  R29, R0,  1);
  andr(   R17, R21, R16);
  and_(   R3,  R5,  R15);
  orr(    R2,  R1,  R9);
  or_(    R17, R15, R11);
  xorr(   R19, R18, R10);
  xor_(   R31, R21, R11);
  nand(   R5,  R7,  R3);
  nand_(  R3,  R1,  R0);
  nor(    R2,  R3,  R5);
  nor_(   R3,  R6,  R8);
  andc(   R25, R12, R11);
  andc_(  R24, R22, R21);
  orc(    R20, R10, R12);
  orc_(   R22, R2,  R13);

  nop();

  // PPC 1, section 3.3.12, Fixed-Point Rotate and Shift Instructions
  sld(    R5,  R6,  R8);
  sld_(   R3,  R5,  R9);
  slw(    R2,  R1,  R10);
  slw_(   R6,  R26, R16);
  srd(    R16, R24, R8);
  srd_(   R21, R14, R7);
  srw(    R22, R25, R29);
  srw_(   R5,  R18, R17);
  srad(   R7,  R11, R0);
  srad_(  R9,  R13, R1);
  sraw(   R7,  R15, R2);
  sraw_(  R4,  R17, R3);
  sldi(   R3,  R18, 63);
  sldi_(  R2,  R20, 30);
  slwi(   R1,  R21, 30);
  slwi_(  R7,  R23, 8);
  srdi(   R0,  R19, 2);
  srdi_(  R12, R24, 5);
  srwi(   R13, R27, 6);
  srwi_(  R14, R29, 7);
  sradi(  R15, R30, 9);
  sradi_( R16, R31, 19);
  srawi(  R17, R31, 15);
  srawi_( R18, R31, 12);

  clrrdi( R3, R30, 5);
  clrldi( R9, R10, 11);

  rldicr( R19, R20, 13, 15);
  rldicr_(R20, R20, 16, 14);
  rldicl( R21, R21, 30, 33);
  rldicl_(R22, R1,  20, 25);
  rlwinm( R23, R2,  25, 10, 11);
  rlwinm_(R24, R3,  12, 13, 14);

  // PPC 1, section 3.3.2 Fixed-Point Load Instructions
  lwzx(   R3,  R5, R7);
  lwz(    R11,  0, R1);
  lwzu(   R31, -4, R11);

  lwax(   R3,  R5, R7);
  lwa(    R31, -4, R11);
  lhzx(   R3,  R5, R7);
  lhz(    R31, -4, R11);
  lhzu(   R31, -4, R11);


  lhax(   R3,  R5, R7);
  lha(    R31, -4, R11);
  lhau(   R11,  0, R1);

  lbzx(   R3,  R5, R7);
  lbz(    R31, -4, R11);
  lbzu(   R11,  0, R1);

  ld(     R31, -4, R11);
  ldx(    R3,  R5, R7);
  ldu(    R31, -4, R11);

  //  PPC 1, section 3.3.3 Fixed-Point Store Instructions
  stwx(   R3,  R5, R7);
  stw(    R31, -4, R11);
  stwu(   R11,  0, R1);

  sthx(   R3,  R5, R7 );
  sth(    R31, -4, R11);
  sthu(   R31, -4, R11);

  stbx(   R3,  R5, R7);
  stb(    R31, -4, R11);
  stbu(   R31, -4, R11);

  std(    R31, -4, R11);
  stdx(   R3,  R5, R7);
  stdu(   R31, -4, R11);

 // PPC 1, section 3.3.13 Move To/From System Register Instructions
  mtlr(   R3);
  mflr(   R3);
  mtctr(  R3);
  mfctr(  R3);
  mtcrf(  0xff, R15);
  mtcr(   R15);
  mtcrf(  0x03, R15);
  mtcr(   R15);
  mfcr(   R15);

 // PPC 1, section 2.4.1 Branch Instructions
  Label lbl1, lbl2, lbl3;
  bind(lbl1);

  b(pc());
  b(pc() - 8);
  b(lbl1);
  b(lbl2);
  b(lbl3);

  bl(pc() - 8);
  bl(lbl1);
  bl(lbl2);

  bcl(4, 10, pc() - 8);
  bcl(4, 10, lbl1);
  bcl(4, 10, lbl2);

  bclr( 4, 6, 0);
  bclrl(4, 6, 0);

  bind(lbl2);

  bcctr( 4, 6, 0);
  bcctrl(4, 6, 0);

  blt(CCR0, lbl2);
  bgt(CCR1, lbl2);
  beq(CCR2, lbl2);
  bso(CCR3, lbl2);
  bge(CCR4, lbl2);
  ble(CCR5, lbl2);
  bne(CCR6, lbl2);
  bns(CCR7, lbl2);

  bltl(CCR0, lbl2);
  bgtl(CCR1, lbl2);
  beql(CCR2, lbl2);
  bsol(CCR3, lbl2);
  bgel(CCR4, lbl2);
  blel(CCR5, lbl2);
  bnel(CCR6, lbl2);
  bnsl(CCR7, lbl2);
  blr();

  sync();
  icbi( R1, R2);
  dcbst(R2, R3);

  // FLOATING POINT instructions ppc.
  // PPC 1, section 4.6.2 Floating-Point Load Instructions
  lfs( F1, -11, R3);
  lfsu(F2, 123, R4);
  lfsx(F3, R5,  R6);
  lfd( F4, 456, R7);
  lfdu(F5, 789, R8);
  lfdx(F6, R10, R11);

  // PPC 1, section 4.6.3 Floating-Point Store Instructions
  stfs(  F7,  876, R12);
  stfsu( F8,  543, R13);
  stfsx( F9,  R14, R15);
  stfd(  F10, 210, R16);
  stfdu( F11, 111, R17);
  stfdx( F12, R18, R19);

  // PPC 1, section 4.6.4 Floating-Point Move Instructions
  fmr(   F13, F14);
  fmr_(  F14, F15);
  fneg(  F16, F17);
  fneg_( F18, F19);
  fabs(  F20, F21);
  fabs_( F22, F23);
  fnabs( F24, F25);
  fnabs_(F26, F27);

  // PPC 1, section 4.6.5.1 Floating-Point Elementary Arithmetic
  // Instructions
  fadd(  F28, F29, F30);
  fadd_( F31, F0,  F1);
  fadds( F2,  F3,  F4);
  fadds_(F5,  F6,  F7);
  fsub(  F8,  F9,  F10);
  fsub_( F11, F12, F13);
  fsubs( F14, F15, F16);
  fsubs_(F17, F18, F19);
  fmul(  F20, F21, F22);
  fmul_( F23, F24, F25);
  fmuls( F26, F27, F28);
  fmuls_(F29, F30, F31);
  fdiv(  F0,  F1,  F2);
  fdiv_( F3,  F4,  F5);
  fdivs( F6,  F7,  F8);
  fdivs_(F9,  F10, F11);

  // PPC 1, section 4.6.6 Floating-Point Rounding and Conversion
  // Instructions
  frsp(  F12, F13);
  fctid( F14, F15);
  fctidz(F16, F17);
  fctiw( F18, F19);
  fctiwz(F20, F21);
  fcfid( F22, F23);

  // PPC 1, section 4.6.7 Floating-Point Compare Instructions
  fcmpu( CCR7, F24, F25);

  tty->print_cr("\ntest_asm disassembly (0x%lx 0x%lx):", p2i(code()->insts_begin()), p2i(code()->insts_end()));
  code()->decode();
}

#endif // !PRODUCT
