gt:             assert assertion.checkOperands(this, size, resultReg, inputReg);
context: /*
   * Copyright (c) 2009, 2015, Oracle and/or its affiliates. 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.
   */
  package com.oracle.graal.asm.amd64;
  
  import static com.oracle.graal.asm.NumUtil.isByte;
  import static com.oracle.graal.asm.NumUtil.isInt;
  import static com.oracle.graal.asm.NumUtil.isShiftCount;
  import static com.oracle.graal.asm.amd64.AMD64AsmOptions.UseAddressNop;
  import static com.oracle.graal.asm.amd64.AMD64AsmOptions.UseNormalNop;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.AMD64BinaryArithmetic.ADD;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.AMD64BinaryArithmetic.AND;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.AMD64BinaryArithmetic.CMP;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.AMD64BinaryArithmetic.SUB;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.AMD64BinaryArithmetic.XOR;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.AMD64MOp.DEC;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.AMD64MOp.INC;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.OperandSize.BYTE;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.OperandSize.DWORD;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.OperandSize.PD;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.OperandSize.PS;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.OperandSize.QWORD;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.OperandSize.SD;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.OperandSize.SS;
  import static com.oracle.graal.asm.amd64.AMD64Assembler.OperandSize.WORD;
  import static jdk.vm.ci.amd64.AMD64.CPU;
  import static jdk.vm.ci.amd64.AMD64.XMM;
  import static jdk.vm.ci.amd64.AMD64.r12;
  import static jdk.vm.ci.amd64.AMD64.r13;
  import static jdk.vm.ci.amd64.AMD64.rbp;
  import static jdk.vm.ci.amd64.AMD64.rip;
  import static jdk.vm.ci.amd64.AMD64.rsp;
  import static jdk.vm.ci.code.MemoryBarriers.STORE_LOAD;
  import jdk.vm.ci.amd64.AMD64;
  import jdk.vm.ci.amd64.AMD64.CPUFeature;
  import jdk.vm.ci.code.Register;
  import jdk.vm.ci.code.Register.RegisterCategory;
  import jdk.vm.ci.code.RegisterConfig;
  import jdk.vm.ci.code.TargetDescription;
  
  import com.oracle.graal.asm.Assembler;
  import com.oracle.graal.asm.Label;
  import com.oracle.graal.asm.NumUtil;
  
  /**
   * This class implements an assembler that can encode most X86 instructions.
   */
  public class AMD64Assembler extends Assembler {
  
      private static final int MinEncodingNeedsRex = 8;
  
      /**
       * A sentinel value used as a place holder in an instruction stream for an address that will be
       * patched.
       */
      private static final AMD64Address Placeholder = new AMD64Address(rip);
  
      /**
       * The x86 condition codes used for conditional jumps/moves.
       */
      public enum ConditionFlag {
          Zero(0x4, "|zero|"),
          NotZero(0x5, "|nzero|"),
          Equal(0x4, "="),
          NotEqual(0x5, "!="),
          Less(0xc, "<"),
          LessEqual(0xe, "<="),
          Greater(0xf, ">"),
          GreaterEqual(0xd, ">="),
          Below(0x2, "|<|"),
          BelowEqual(0x6, "|<=|"),
          Above(0x7, "|>|"),
          AboveEqual(0x3, "|>=|"),
          Overflow(0x0, "|of|"),
          NoOverflow(0x1, "|nof|"),
          CarrySet(0x2, "|carry|"),
          CarryClear(0x3, "|ncarry|"),
          Negative(0x8, "|neg|"),
          Positive(0x9, "|pos|"),
          Parity(0xa, "|par|"),
          NoParity(0xb, "|npar|");
  
          private final int value;
          private final String operator;
  
          ConditionFlag(int value, String operator) {
              this.value = value;
              this.operator = operator;
          }
  
          public ConditionFlag negate() {
              switch (this) {
                  case Zero:
                      return NotZero;
                  case NotZero:
                      return Zero;
                  case Equal:
                      return NotEqual;
                  case NotEqual:
                      return Equal;
                  case Less:
                      return GreaterEqual;
                  case LessEqual:
                      return Greater;
                  case Greater:
                      return LessEqual;
                  case GreaterEqual:
                      return Less;
                  case Below:
                      return AboveEqual;
                  case BelowEqual:
                      return Above;
                  case Above:
                      return BelowEqual;
                  case AboveEqual:
                      return Below;
                  case Overflow:
                      return NoOverflow;
                  case NoOverflow:
                      return Overflow;
                  case CarrySet:
                      return CarryClear;
                  case CarryClear:
                      return CarrySet;
                  case Negative:
                      return Positive;
                  case Positive:
                      return Negative;
                  case Parity:
                      return NoParity;
                  case NoParity:
                      return Parity;
              }
              throw new IllegalArgumentException();
          }
  
          public int getValue() {
              return value;
          }
  
          @Override
          public String toString() {
              return operator;
          }
      }
  
      /**
       * Constants for X86 prefix bytes.
       */
      private static class Prefix {
  
          private static final int REX = 0x40;
          private static final int REXB = 0x41;
          private static final int REXX = 0x42;
          private static final int REXXB = 0x43;
          private static final int REXR = 0x44;
          private static final int REXRB = 0x45;
          private static final int REXRX = 0x46;
          private static final int REXRXB = 0x47;
          private static final int REXW = 0x48;
          private static final int REXWB = 0x49;
          private static final int REXWX = 0x4A;
          private static final int REXWXB = 0x4B;
          private static final int REXWR = 0x4C;
          private static final int REXWRB = 0x4D;
          private static final int REXWRX = 0x4E;
          private static final int REXWRXB = 0x4F;
      }
  
      /**
       * The x86 operand sizes.
       */
      public enum OperandSize {
          BYTE(1) {
              @Override
              protected void emitImmediate(AMD64Assembler asm, int imm) {
                  assert imm == (byte) imm;
                  asm.emitByte(imm);
              }
          },
  
          WORD(2, 0x66) {
              @Override
              protected void emitImmediate(AMD64Assembler asm, int imm) {
                  assert imm == (short) imm;
                  asm.emitShort(imm);
              }
          },
  
          DWORD(4) {
              @Override
              protected void emitImmediate(AMD64Assembler asm, int imm) {
                  asm.emitInt(imm);
              }
          },
  
          QWORD(8) {
              @Override
              protected void emitImmediate(AMD64Assembler asm, int imm) {
                  asm.emitInt(imm);
              }
          },
  
          SS(4, 0xF3, true),
  
          SD(8, 0xF2, true),
  
          PS(16, true),
  
          PD(16, 0x66, true);
  
          private final int sizePrefix;
  
          private final int bytes;
          private final boolean xmm;
  
          OperandSize(int bytes) {
              this(bytes, 0);
          }
  
          OperandSize(int bytes, int sizePrefix) {
              this(bytes, sizePrefix, false);
          }
  
          OperandSize(int bytes, boolean xmm) {
              this(bytes, 0, xmm);
          }
  
          OperandSize(int bytes, int sizePrefix, boolean xmm) {
              this.sizePrefix = sizePrefix;
              this.bytes = bytes;
              this.xmm = xmm;
          }
  
          public int getBytes() {
              return bytes;
          }
  
          public boolean isXmmType() {
              return xmm;
          }
  
          /**
           * Emit an immediate of this size. Note that immediate {@link #QWORD} operands are encoded
           * as sign-extended 32-bit values.
           *
           * @param asm
           * @param imm
           */
          protected void emitImmediate(AMD64Assembler asm, int imm) {
              assert false;
          }
      }
  
      /**
       * Operand size and register type constraints.
       */
      private enum OpAssertion {
          ByteAssertion(CPU, CPU, BYTE),
          IntegerAssertion(CPU, CPU, WORD, DWORD, QWORD),
          No16BitAssertion(CPU, CPU, DWORD, QWORD),
          No32BitAssertion(CPU, CPU, WORD, QWORD),
          QwordOnlyAssertion(CPU, CPU, QWORD),
          FloatingAssertion(XMM, XMM, SS, SD, PS, PD),
          PackedFloatingAssertion(XMM, XMM, PS, PD),
          SingleAssertion(XMM, XMM, SS),
          DoubleAssertion(XMM, XMM, SD),
          IntToFloatingAssertion(XMM, CPU, DWORD, QWORD),
          FloatingToIntAssertion(CPU, XMM, DWORD, QWORD);
  
          private final RegisterCategory resultCategory;
          private final RegisterCategory inputCategory;
          private final OperandSize[] allowedSizes;
  
          OpAssertion(RegisterCategory resultCategory, RegisterCategory inputCategory, OperandSize... allowedSizes) {
              this.resultCategory = resultCategory;
              this.inputCategory = inputCategory;
              this.allowedSizes = allowedSizes;
          }
  
          protected boolean checkOperands(AMD64Op op, OperandSize size, Register resultReg, Register inputReg) {
              assert resultReg == null || resultCategory.equals(resultReg.getRegisterCategory()) : "invalid result register " + resultReg + " used in " + op;
              assert inputReg == null || inputCategory.equals(inputReg.getRegisterCategory()) : "invalid input register " + inputReg + " used in " + op;
  
              for (OperandSize s : allowedSizes) {
                  if (size == s) {
                      return true;
                  }
              }
  
              assert false : "invalid operand size " + size + " used in " + op;
              return false;
          }
      }
  
      /**
       * The register to which {@link Register#Frame} and {@link Register#CallerFrame} are bound.
       */
      public final Register frameRegister;
  
      /**
       * Constructs an assembler for the AMD64 architecture.
       *
       * @param registerConfig the register configuration used to bind {@link Register#Frame} and
       *            {@link Register#CallerFrame} to physical registers. This value can be null if this
       *            assembler instance will not be used to assemble instructions using these logical
       *            registers.
       */
      public AMD64Assembler(TargetDescription target, RegisterConfig registerConfig) {
          super(target);
          this.frameRegister = registerConfig == null ? null : registerConfig.getFrameRegister();
      }
  
      private boolean supports(CPUFeature feature) {
          return ((AMD64) target.arch).getFeatures().contains(feature);
      }
  
      private static int encode(Register r) {
          assert r.encoding < 16 && r.encoding >= 0 : "encoding out of range: " + r.encoding;
          return r.encoding & 0x7;
      }
  
      /**
       * Get RXB bits for register-register instruction. In that encoding, ModRM.rm contains a
       * register index. The R bit extends the ModRM.reg field and the B bit extends the ModRM.rm
       * field. The X bit must be 0.
       */
      protected static int getRXB(Register reg, Register rm) {
          int rxb = (reg == null ? 0 : reg.encoding & 0x08) >> 1;
          rxb |= (rm == null ? 0 : rm.encoding & 0x08) >> 3;
          return rxb;
      }
  
      /**
       * Get RXB bits for register-memory instruction. The R bit extends the ModRM.reg field. There
       * are two cases for the memory operand:<br>
       * ModRM.rm contains the base register: In that case, B extends the ModRM.rm field and X = 0. <br>
       * There is an SIB byte: In that case, X extends SIB.index and B extends SIB.base.
       */
      protected static int getRXB(Register reg, AMD64Address rm) {
          int rxb = (reg == null ? 0 : reg.encoding & 0x08) >> 1;
          if (!rm.getIndex().equals(Register.None)) {
              rxb |= (rm.getIndex().encoding & 0x08) >> 2;
          }
          if (!rm.getBase().equals(Register.None)) {
              rxb |= (rm.getBase().encoding & 0x08) >> 3;
          }
          return rxb;
      }
  
      /**
       * Emit the ModR/M byte for one register operand and an opcode extension in the R field.
       * <p>
       * Format: [ 11 reg r/m ]
       */
      protected void emitModRM(int reg, Register rm) {
          assert (reg & 0x07) == reg;
          emitByte(0xC0 | (reg << 3) | (rm.encoding & 0x07));
      }
  
      /**
       * Emit the ModR/M byte for two register operands.
       * <p>
       * Format: [ 11 reg r/m ]
       */
      protected void emitModRM(Register reg, Register rm) {
          emitModRM(reg.encoding & 0x07, rm);
      }
  
      protected void emitOperandHelper(Register reg, AMD64Address addr) {
          assert !reg.equals(Register.None);
          emitOperandHelper(encode(reg), addr, false);
      }
  
      /**
       * Emits the ModR/M byte and optionally the SIB byte for one register and one memory operand.
       *
       * @param force4Byte use 4 byte encoding for displacements that would normally fit in a byte
       */
      protected void emitOperandHelper(Register reg, AMD64Address addr, boolean force4Byte) {
          assert !reg.equals(Register.None);
          emitOperandHelper(encode(reg), addr, force4Byte);
      }
  
      protected void emitOperandHelper(int reg, AMD64Address addr) {
          emitOperandHelper(reg, addr, false);
      }
  
      /**
       * Emits the ModR/M byte and optionally the SIB byte for one memory operand and an opcode
       * extension in the R field.
       *
       * @param force4Byte use 4 byte encoding for displacements that would normally fit in a byte
       */
      protected void emitOperandHelper(int reg, AMD64Address addr, boolean force4Byte) {
          assert (reg & 0x07) == reg;
          int regenc = reg << 3;
  
          Register base = addr.getBase();
          Register index = addr.getIndex();
  
          AMD64Address.Scale scale = addr.getScale();
          int disp = addr.getDisplacement();
  
          if (base.equals(Register.Frame)) {
              assert frameRegister != null : "cannot use register " + Register.Frame + " in assembler with null register configuration";
              base = frameRegister;
          }
  
          if (base.equals(AMD64.rip)) { // also matches Placeholder
              // [00 000 101] disp32
              assert index.equals(Register.None) : "cannot use RIP relative addressing with index register";
              emitByte(0x05 | regenc);
              emitInt(disp);
          } else if (base.isValid()) {
              int baseenc = base.isValid() ? encode(base) : 0;
              if (index.isValid()) {
                  int indexenc = encode(index) << 3;
                  // [base + indexscale + disp]
                  if (disp == 0 && !base.equals(rbp) && !base.equals(r13)) {
                      // [base + indexscale]
                      // [00 reg 100][ss index base]
                      assert !index.equals(rsp) : "illegal addressing mode";
                      emitByte(0x04 | regenc);
                      emitByte(scale.log2 << 6 | indexenc | baseenc);
                  } else if (isByte(disp) && !force4Byte) {
                      // [base + indexscale + imm8]
                      // [01 reg 100][ss index base] imm8
                      assert !index.equals(rsp) : "illegal addressing mode";
                      emitByte(0x44 | regenc);
                      emitByte(scale.log2 << 6 | indexenc | baseenc);
                      emitByte(disp & 0xFF);
                  } else {
                      // [base + indexscale + disp32]
                      // [10 reg 100][ss index base] disp32
                      assert !index.equals(rsp) : "illegal addressing mode";
                      emitByte(0x84 | regenc);
                      emitByte(scale.log2 << 6 | indexenc | baseenc);
                      emitInt(disp);
                  }
              } else if (base.equals(rsp) || base.equals(r12)) {
                  // [rsp + disp]
                  if (disp == 0) {
                      // [rsp]
                      // [00 reg 100][00 100 100]
                      emitByte(0x04 | regenc);
                      emitByte(0x24);
                  } else if (isByte(disp) && !force4Byte) {
                      // [rsp + imm8]
                      // [01 reg 100][00 100 100] disp8
                      emitByte(0x44 | regenc);
                      emitByte(0x24);
                      emitByte(disp & 0xFF);
                  } else {
                      // [rsp + imm32]
                      // [10 reg 100][00 100 100] disp32
                      emitByte(0x84 | regenc);
                      emitByte(0x24);
                      emitInt(disp);
                  }
              } else {
                  // [base + disp]
                  assert !base.equals(rsp) && !base.equals(r12) : "illegal addressing mode";
                  if (disp == 0 && !base.equals(rbp) && !base.equals(r13)) {
                      // [base]
                      // [00 reg base]
                      emitByte(0x00 | regenc | baseenc);
                  } else if (isByte(disp) && !force4Byte) {
                      // [base + disp8]
                      // [01 reg base] disp8
                      emitByte(0x40 | regenc | baseenc);
                      emitByte(disp & 0xFF);
                  } else {
                      // [base + disp32]
                      // [10 reg base] disp32
                      emitByte(0x80 | regenc | baseenc);
                      emitInt(disp);
                  }
              }
          } else {
              if (index.isValid()) {
                  int indexenc = encode(index) << 3;
                  // [indexscale + disp]
                  // [00 reg 100][ss index 101] disp32
                  assert !index.equals(rsp) : "illegal addressing mode";
                  emitByte(0x04 | regenc);
                  emitByte(scale.log2 << 6 | indexenc | 0x05);
                  emitInt(disp);
              } else {
                  // [disp] ABSOLUTE
                  // [00 reg 100][00 100 101] disp32
                  emitByte(0x04 | regenc);
                  emitByte(0x25);
                  emitInt(disp);
              }
          }
      }
  
      /**
       * Base class for AMD64 opcodes.
       */
      public static class AMD64Op {
  
          protected static final int P_0F = 0x0F;
          protected static final int P_0F38 = 0x380F;
          protected static final int P_0F3A = 0x3A0F;
  
          private final String opcode;
  
          private final int prefix1;
          private final int prefix2;
          private final int op;
  
          private final boolean dstIsByte;
          private final boolean srcIsByte;
  
          private final OpAssertion assertion;
          private final CPUFeature feature;
  
          protected AMD64Op(String opcode, int prefix1, int prefix2, int op, OpAssertion assertion, CPUFeature feature) {
              this(opcode, prefix1, prefix2, op, assertion == OpAssertion.ByteAssertion, assertion == OpAssertion.ByteAssertion, assertion, feature);
          }
  
          protected AMD64Op(String opcode, int prefix1, int prefix2, int op, boolean dstIsByte, boolean srcIsByte, OpAssertion assertion, CPUFeature feature) {
              this.opcode = opcode;
              this.prefix1 = prefix1;
              this.prefix2 = prefix2;
              this.op = op;
  
              this.dstIsByte = dstIsByte;
              this.srcIsByte = srcIsByte;
  
              this.assertion = assertion;
              this.feature = feature;
          }
  
          protected final void emitOpcode(AMD64Assembler asm, OperandSize size, int rxb, int dstEnc, int srcEnc) {
              if (prefix1 != 0) {
                  asm.emitByte(prefix1);
              }
              if (size.sizePrefix != 0) {
                  asm.emitByte(size.sizePrefix);
              }
              int rexPrefix = 0x40 | rxb;
              if (size == QWORD) {
                  rexPrefix |= 0x08;
              }
              if (rexPrefix != 0x40 || (dstIsByte && dstEnc >= 4) || (srcIsByte && srcEnc >= 4)) {
                  asm.emitByte(rexPrefix);
              }
              if (prefix2 > 0xFF) {
                  asm.emitShort(prefix2);
              } else if (prefix2 > 0) {
                  asm.emitByte(prefix2);
              }
              asm.emitByte(op);
          }
  
          protected final boolean verify(AMD64Assembler asm, OperandSize size, Register resultReg, Register inputReg) {
              assert feature == null || asm.supports(feature) : String.format("unsupported feature %s required for %s", feature, opcode);
  