[AArch64][SVE 15/32] Add {insert,extract}_all_fields helpers
[binutils-gdb.git] / opcodes / aarch64-asm.c
index 27a4def173daf972ee523c73d79267e7f3292e73..3b0a38373fcb8416a5ca942fffa82ea5e52702cb 100644 (file)
@@ -1,5 +1,5 @@
 /* aarch64-asm.c -- AArch64 assembler support.
 /* aarch64-asm.c -- AArch64 assembler support.
-   Copyright 2012-2013  Free Software Foundation, Inc.
+   Copyright (C) 2012-2016 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of the GNU opcodes library.
    Contributed by ARM Ltd.
 
    This file is part of the GNU opcodes library.
@@ -20,6 +20,7 @@
 
 #include "sysdep.h"
 #include <stdarg.h>
 
 #include "sysdep.h"
 #include <stdarg.h>
+#include "libiberty.h"
 #include "aarch64-asm.h"
 
 /* Utilities.  */
 #include "aarch64-asm.h"
 
 /* Utilities.  */
@@ -55,6 +56,25 @@ insert_fields (aarch64_insn *code, aarch64_insn value, aarch64_insn mask, ...)
   va_end (va);
 }
 
   va_end (va);
 }
 
+/* Insert a raw field value VALUE into all fields in SELF->fields.
+   The least significant bit goes in the final field.  */
+
+static void
+insert_all_fields (const aarch64_operand *self, aarch64_insn *code,
+                  aarch64_insn value)
+{
+  unsigned int i;
+  enum aarch64_field_kind kind;
+
+  for (i = ARRAY_SIZE (self->fields); i-- > 0; )
+    if (self->fields[i] != FLD_NIL)
+      {
+       kind = self->fields[i];
+       insert_field (kind, code, value, 0);
+       value >>= fields[kind].width;
+      }
+}
+
 /* Operand inserters.  */
 
 /* Insert register number.  */
 /* Operand inserters.  */
 
 /* Insert register number.  */
@@ -318,17 +338,11 @@ aarch64_ins_imm (const aarch64_operand *self, const aarch64_opnd_info *info,
                 const aarch64_inst *inst ATTRIBUTE_UNUSED)
 {
   int64_t imm;
                 const aarch64_inst *inst ATTRIBUTE_UNUSED)
 {
   int64_t imm;
-  /* Maximum of two fields to insert.  */
-  assert (self->fields[2] == FLD_NIL);
 
   imm = info->imm.value;
   if (operand_need_shift_by_two (self))
     imm >>= 2;
 
   imm = info->imm.value;
   if (operand_need_shift_by_two (self))
     imm >>= 2;
-  if (self->fields[1] == FLD_NIL)
-    insert_field (self->fields[0], code, imm, 0);
-  else
-    /* e.g. TBZ b5:b40.  */
-    insert_fields (code, imm, 0, 2, self->fields[1], self->fields[0]);
+  insert_all_fields (self, code, imm);
   return NULL;
 }
 
   return NULL;
 }
 
@@ -436,11 +450,11 @@ aarch64_ins_limm (const aarch64_operand *self, const aarch64_opnd_info *info,
 {
   aarch64_insn value;
   uint64_t imm = info->imm.value;
 {
   aarch64_insn value;
   uint64_t imm = info->imm.value;
-  int is32 = aarch64_get_qualifier_esize (inst->operands[0].qualifier) == 4;
+  int esize = aarch64_get_qualifier_esize (inst->operands[0].qualifier);
 
   if (inst->opcode->op == OP_BIC)
     imm = ~imm;
 
   if (inst->opcode->op == OP_BIC)
     imm = ~imm;
-  if (aarch64_logical_immediate_p (imm, is32, &value) == FALSE)
+  if (aarch64_logical_immediate_p (imm, esize, &value) == FALSE)
     /* The constraint check should have guaranteed this wouldn't happen.  */
     assert (0);
 
     /* The constraint check should have guaranteed this wouldn't happen.  */
     assert (0);
 
@@ -667,6 +681,19 @@ aarch64_ins_prfop (const aarch64_operand *self ATTRIBUTE_UNUSED,
   return NULL;
 }
 
   return NULL;
 }
 
+/* Encode the hint number for instructions that alias HINT but take an
+   operand.  */
+
+const char *
+aarch64_ins_hint (const aarch64_operand *self ATTRIBUTE_UNUSED,
+                 const aarch64_opnd_info *info, aarch64_insn *code,
+                 const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+  /* CRm:op2.  */
+  insert_fields (code, info->hint_option->value, 0, 2, FLD_op2, FLD_CRm);
+  return NULL;
+}
+
 /* Encode the extended register operand for e.g.
      STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}].  */
 const char *
 /* Encode the extended register operand for e.g.
      STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}].  */
 const char *
@@ -856,6 +883,14 @@ do_special_encoding (struct aarch64_inst *inst)
       if (inst->opcode->flags & F_N)
        insert_field (FLD_N, &inst->value, value, inst->opcode->mask);
     }
       if (inst->opcode->flags & F_N)
        insert_field (FLD_N, &inst->value, value, inst->opcode->mask);
     }
+  if (inst->opcode->flags & F_LSE_SZ)
+    {
+      idx = select_operand_for_sf_field_coding (inst->opcode);
+      value = (inst->operands[idx].qualifier == AARCH64_OPND_QLF_X
+              || inst->operands[idx].qualifier == AARCH64_OPND_QLF_SP)
+       ? 1 : 0;
+      insert_field (FLD_lse_sz, &inst->value, value, 0);
+    }
   if (inst->opcode->flags & F_SIZEQ)
     encode_sizeq (inst);
   if (inst->opcode->flags & F_FPTYPE)
   if (inst->opcode->flags & F_SIZEQ)
     encode_sizeq (inst);
   if (inst->opcode->flags & F_FPTYPE)
@@ -1030,6 +1065,37 @@ convert_bfi_to_bfm (aarch64_inst *inst)
     }
 }
 
     }
 }
 
+/* The instruction written:
+     BFC <Xd>, #<lsb>, #<width>
+   is equivalent to:
+     BFM <Xd>, XZR, #((64-<lsb>)&0x3f), #(<width>-1).  */
+
+static void
+convert_bfc_to_bfm (aarch64_inst *inst)
+{
+  int64_t lsb, width;
+
+  /* Insert XZR.  */
+  copy_operand_info (inst, 3, 2);
+  copy_operand_info (inst, 2, 1);
+  copy_operand_info (inst, 2, 0);
+  inst->operands[1].reg.regno = 0x1f;
+
+  /* Convert the immedate operand.  */
+  lsb = inst->operands[2].imm.value;
+  width = inst->operands[3].imm.value;
+  if (inst->operands[2].qualifier == AARCH64_OPND_QLF_imm_0_31)
+    {
+      inst->operands[2].imm.value = (32 - lsb) & 0x1f;
+      inst->operands[3].imm.value = width - 1;
+    }
+  else
+    {
+      inst->operands[2].imm.value = (64 - lsb) & 0x3f;
+      inst->operands[3].imm.value = width - 1;
+    }
+}
+
 /* The instruction written:
      LSL <Xd>, <Xn>, #<shift>
    is equivalent to:
 /* The instruction written:
      LSL <Xd>, <Xn>, #<shift>
    is equivalent to:
@@ -1163,6 +1229,9 @@ convert_to_real (aarch64_inst *inst, const aarch64_opcode *real)
     case OP_UBFIZ:
       convert_bfi_to_bfm (inst);
       break;
     case OP_UBFIZ:
       convert_bfi_to_bfm (inst);
       break;
+    case OP_BFC:
+      convert_bfc_to_bfm (inst);
+      break;
     case OP_MOV_V:
       convert_mov_to_orr (inst);
       break;
     case OP_MOV_V:
       convert_mov_to_orr (inst);
       break;