[v1,7/8] aarch64: add flag OPD_F_UNSIGNED to distinguish signedness of immediate operands

Message ID 20241023104816.501176-8-matthieu.longo@arm.com
State Committed
Headers
Series small refactorings before posting PAuth_LR patch series |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Matthieu Longo Oct. 23, 2024, 10:48 a.m. UTC
  This patch introduces a new operand flag OPD_F_UNSIGNED to signal that
the immediate value should be treated as an unsigned value. The default
signedness of immediate operands is signed.
---
 opcodes/aarch64-opc.c | 93 ++++++++++++++++++++++++++++++++-----------
 opcodes/aarch64-opc.h |  7 ++++
 2 files changed, 77 insertions(+), 23 deletions(-)
  

Comments

Richard Earnshaw (lists) Nov. 5, 2024, 6:17 p.m. UTC | #1
On 23/10/2024 11:48, Matthieu Longo wrote:
> This patch introduces a new operand flag OPD_F_UNSIGNED to signal that
> the immediate value should be treated as an unsigned value. The default
> signedness of immediate operands is signed.
> ---
>  opcodes/aarch64-opc.c | 93 ++++++++++++++++++++++++++++++++-----------
>  opcodes/aarch64-opc.h |  7 ++++
>  2 files changed, 77 insertions(+), 23 deletions(-)
> 
> diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c
> index 93ae8767dfe..43c7348d331 100644
> --- a/opcodes/aarch64-opc.c
> +++ b/opcodes/aarch64-opc.c
> @@ -1620,6 +1620,55 @@ check_reglist (const aarch64_opnd_info *opnd,
>    return true;
>  }
>  
> +typedef struct
> +{
> +  int64_t min;
> +  int64_t max;
> +} imm_range_t;
> +
> +static imm_range_t
> +imm_range_min_max (unsigned size, bool signed_rng)
> +{
> +  assert (size < 63);
> +  imm_range_t r;
> +  if (signed_rng)
> +    {
> +      r.max = (((int64_t) 0x1) << (size - 1)) - 1;
> +      r.min = - r.max - 1;
> +    }
> +  else
> +    {
> +      r.max = (((int64_t) 0x1) << size) - 1;
> +      r.min = 0;
> +    }
> +  return r;
> +}
> +
> +/* Check that an immediate value is in the range provided by the
> +   operand type.  */
> +static bool
> +check_immediate_out_of_range (int64_t imm,
> +			      enum aarch64_opnd type,
> +			      aarch64_operand_error *mismatch_detail,
> +			      int idx)
> +{
> +  const aarch64_operand *operand = get_operand_from_code (type);
> +  uint8_t size = get_operand_fields_width (operand);
> +  bool unsigned_imm = operand_need_unsigned_offset (operand);
> +  bool (*value_fit_field) (int64_t, unsigned)
> +    = (unsigned_imm
> +      ? value_fit_unsigned_field_p
> +      : value_fit_signed_field_p);
> +
> +  if (!value_fit_field (imm, size))
> +    {
> +      imm_range_t rng = imm_range_min_max (size, !unsigned_imm);
> +      set_imm_out_of_range_error (mismatch_detail, idx, rng.min, rng.max);
> +      return false;
> +    }
> +  return true;
> +}
> +
>  /* Check that indexed ZA operand OPND has:
>  
>     - a selection register in the range [MIN_WREG, MIN_WREG + 3]
> @@ -2375,27 +2424,25 @@ operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx,
>  	case AARCH64_OPND_ADDR_PCREL19:
>  	case AARCH64_OPND_ADDR_PCREL21:
>  	case AARCH64_OPND_ADDR_PCREL26:
> -	  imm = opnd->imm.value;
> -	  if (operand_need_shift_by_two (get_operand_from_code (type)))
> -	    {
> -	      /* The offset value in a PC-relative branch instruction is alway
> -		 4-byte aligned and is encoded without the lowest 2 bits.  */
> -	      if (!value_aligned_p (imm, 4))
> -		{
> -		  set_unaligned_error (mismatch_detail, idx, 4);
> -		  return false;
> -		}
> -	      /* Right shift by 2 so that we can carry out the following check
> -		 canonically.  */
> -	      imm >>= 2;
> -	    }
> -	  size = get_operand_fields_width (get_operand_from_code (type));
> -	  if (!value_fit_signed_field_p (imm, size))
> -	    {
> -	      set_other_error (mismatch_detail, idx,
> -			       _("immediate out of range"));
> +	  {
> +	    imm = opnd->imm.value;
> +	    if (operand_need_shift_by_two (get_operand_from_code (type)))
> +	      {
> +		/* The offset value in a PC-relative branch instruction is alway
> +		   4-byte aligned and is encoded without the lowest 2 bits.  */
> +		if (!value_aligned_p (imm, 4))
> +		  {
> +		    set_unaligned_error (mismatch_detail, idx, 4);
> +		    return false;
> +		  }
> +		/* Right shift by 2 so that we can carry out the following check
> +		   canonically.  */
> +		imm >>= 2;
> +	      }
> +
> +	    if (!check_immediate_out_of_range (imm, type, mismatch_detail, idx))
>  	      return false;
> -	    }
> +	  }
>  	  break;
>  
>  	case AARCH64_OPND_SME_ADDR_RI_U4xVL:
> @@ -2809,9 +2856,9 @@ operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx,
>  	  assert (size < 32);
>  	  if (!value_fit_signed_field_p (opnd->imm.value, size))
>  	    {
> -	      set_imm_out_of_range_error (mismatch_detail, idx,
> -					  -(1 << (size - 1)),
> -					  (1 << (size - 1)) - 1);
> +	      imm_range_t rng = imm_range_min_max (size, true);
> +	      set_imm_out_of_range_error (mismatch_detail, idx, rng.min,
> +					  rng.max);
>  	      return false;
>  	    }
>  	  break;
> diff --git a/opcodes/aarch64-opc.h b/opcodes/aarch64-opc.h
> index d600f40cf5c..e7495cdb5fb 100644
> --- a/opcodes/aarch64-opc.h
> +++ b/opcodes/aarch64-opc.h
> @@ -298,6 +298,7 @@ verify_constraints (const struct aarch64_inst *, const aarch64_insn, bfd_vma,
>  #define OPD_F_SHIFT_BY_4	0x00000800	/* Need to left shift the field
>  						   value by 4 to get the value
>  						   of an immediate operand.  */
> +#define OPD_F_UNSIGNED		0x00001000	/* Expect an unsigned value.  */
>  
>  
>  /* Register flags.  */
> @@ -401,6 +402,12 @@ operand_need_shift_by_four (const aarch64_operand *operand)
>    return (operand->flags & OPD_F_SHIFT_BY_4) != 0;
>  }
>  
> +static inline bool
> +operand_need_unsigned_offset (const aarch64_operand *operand)
> +{
> +  return (operand->flags & OPD_F_UNSIGNED) != 0;
> +}
> +
>  static inline bool
>  operand_maybe_stack_pointer (const aarch64_operand *operand)
>  {

OK.

R.
  

Patch

diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c
index 93ae8767dfe..43c7348d331 100644
--- a/opcodes/aarch64-opc.c
+++ b/opcodes/aarch64-opc.c
@@ -1620,6 +1620,55 @@  check_reglist (const aarch64_opnd_info *opnd,
   return true;
 }
 
+typedef struct
+{
+  int64_t min;
+  int64_t max;
+} imm_range_t;
+
+static imm_range_t
+imm_range_min_max (unsigned size, bool signed_rng)
+{
+  assert (size < 63);
+  imm_range_t r;
+  if (signed_rng)
+    {
+      r.max = (((int64_t) 0x1) << (size - 1)) - 1;
+      r.min = - r.max - 1;
+    }
+  else
+    {
+      r.max = (((int64_t) 0x1) << size) - 1;
+      r.min = 0;
+    }
+  return r;
+}
+
+/* Check that an immediate value is in the range provided by the
+   operand type.  */
+static bool
+check_immediate_out_of_range (int64_t imm,
+			      enum aarch64_opnd type,
+			      aarch64_operand_error *mismatch_detail,
+			      int idx)
+{
+  const aarch64_operand *operand = get_operand_from_code (type);
+  uint8_t size = get_operand_fields_width (operand);
+  bool unsigned_imm = operand_need_unsigned_offset (operand);
+  bool (*value_fit_field) (int64_t, unsigned)
+    = (unsigned_imm
+      ? value_fit_unsigned_field_p
+      : value_fit_signed_field_p);
+
+  if (!value_fit_field (imm, size))
+    {
+      imm_range_t rng = imm_range_min_max (size, !unsigned_imm);
+      set_imm_out_of_range_error (mismatch_detail, idx, rng.min, rng.max);
+      return false;
+    }
+  return true;
+}
+
 /* Check that indexed ZA operand OPND has:
 
    - a selection register in the range [MIN_WREG, MIN_WREG + 3]
@@ -2375,27 +2424,25 @@  operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx,
 	case AARCH64_OPND_ADDR_PCREL19:
 	case AARCH64_OPND_ADDR_PCREL21:
 	case AARCH64_OPND_ADDR_PCREL26:
-	  imm = opnd->imm.value;
-	  if (operand_need_shift_by_two (get_operand_from_code (type)))
-	    {
-	      /* The offset value in a PC-relative branch instruction is alway
-		 4-byte aligned and is encoded without the lowest 2 bits.  */
-	      if (!value_aligned_p (imm, 4))
-		{
-		  set_unaligned_error (mismatch_detail, idx, 4);
-		  return false;
-		}
-	      /* Right shift by 2 so that we can carry out the following check
-		 canonically.  */
-	      imm >>= 2;
-	    }
-	  size = get_operand_fields_width (get_operand_from_code (type));
-	  if (!value_fit_signed_field_p (imm, size))
-	    {
-	      set_other_error (mismatch_detail, idx,
-			       _("immediate out of range"));
+	  {
+	    imm = opnd->imm.value;
+	    if (operand_need_shift_by_two (get_operand_from_code (type)))
+	      {
+		/* The offset value in a PC-relative branch instruction is alway
+		   4-byte aligned and is encoded without the lowest 2 bits.  */
+		if (!value_aligned_p (imm, 4))
+		  {
+		    set_unaligned_error (mismatch_detail, idx, 4);
+		    return false;
+		  }
+		/* Right shift by 2 so that we can carry out the following check
+		   canonically.  */
+		imm >>= 2;
+	      }
+
+	    if (!check_immediate_out_of_range (imm, type, mismatch_detail, idx))
 	      return false;
-	    }
+	  }
 	  break;
 
 	case AARCH64_OPND_SME_ADDR_RI_U4xVL:
@@ -2809,9 +2856,9 @@  operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx,
 	  assert (size < 32);
 	  if (!value_fit_signed_field_p (opnd->imm.value, size))
 	    {
-	      set_imm_out_of_range_error (mismatch_detail, idx,
-					  -(1 << (size - 1)),
-					  (1 << (size - 1)) - 1);
+	      imm_range_t rng = imm_range_min_max (size, true);
+	      set_imm_out_of_range_error (mismatch_detail, idx, rng.min,
+					  rng.max);
 	      return false;
 	    }
 	  break;
diff --git a/opcodes/aarch64-opc.h b/opcodes/aarch64-opc.h
index d600f40cf5c..e7495cdb5fb 100644
--- a/opcodes/aarch64-opc.h
+++ b/opcodes/aarch64-opc.h
@@ -298,6 +298,7 @@  verify_constraints (const struct aarch64_inst *, const aarch64_insn, bfd_vma,
 #define OPD_F_SHIFT_BY_4	0x00000800	/* Need to left shift the field
 						   value by 4 to get the value
 						   of an immediate operand.  */
+#define OPD_F_UNSIGNED		0x00001000	/* Expect an unsigned value.  */
 
 
 /* Register flags.  */
@@ -401,6 +402,12 @@  operand_need_shift_by_four (const aarch64_operand *operand)
   return (operand->flags & OPD_F_SHIFT_BY_4) != 0;
 }
 
+static inline bool
+operand_need_unsigned_offset (const aarch64_operand *operand)
+{
+  return (operand->flags & OPD_F_UNSIGNED) != 0;
+}
+
 static inline bool
 operand_maybe_stack_pointer (const aarch64_operand *operand)
 {