@@ -897,10 +897,7 @@ (define_insn "@aarch64_sme_<optab><mode><mode>"
(const_int 1)]
SME_READZ_HV))]
"TARGET_STREAMING_SME2p1"
- {
- operands[3] = GEN_INT (<vector_count> - 1);
- return "movaz\t%0, za%1<hv>.<Vetype>[%w2, 0:%3]";
- }
+ "movaz\t%0, za%1<hv>.<Vetype>[%w2, 0:%[SME_VC]0]"
)
(define_insn "*aarch64_sme_<optab><mode><mode>_plus"
@@ -12955,67 +12955,178 @@ sizetochar (int size)
}
}
+/* Print operands supported by AArch64. */
+typedef enum aarch64_print_operands {
+ /* An integer or symbol address without a preceding # sign. */
+ A64_PRINT_constants,
+ /* Print the sign/zero-extend size as a character 8->b, 16->h, 32->w. Can
+ also be used for masks: 0xff->b, 0xffff->h, 0xffffffff->w. */
+ A64_PRINT_extended,
+ /* Prints N such that 2^N == X (X must be power of 2 and const int). */
+ A64_PRINT_power2n,
+ /* Print the number of non-zero bits in X (a const_int). */
+ A64_PRINT_num_nonzero,
+ /* Print the higher numbered register of a pair (TImode) of regs. */
+ A64_PRINT_hi_regpair,
+ /* If the operand is a duplicated vector constant, replace it with the
+ duplicated scalar. If the operand is then a floating-point constant,
+ replace it with the integer bit representation. Print the transformed
+ constant as a signed decimal number. */
+ A64_PRINT_dup_or_int,
+ /* Print a condition (eq, ne, etc). */
+ A64_PRINT_cc_code,
+ /* Same as 'm', but invert condition. */
+ A64_PRINT_inv_cc_code,
+ /* Take the duplicated element in a vector constant and print the negative
+ of it in decimal. */
+ A64_PRINT_neg_dup_const,
+ /* Print a scalar FP/SIMD register name. */
+ A64_PRINT_scalar_b_reg,
+ /* Print a scalar FP/SIMD register name. */
+ A64_PRINT_scalar_h_reg,
+ /* Print a scalar FP/SIMD register name. */
+ A64_PRINT_scalar_s_reg,
+ /* Print a scalar FP/SIMD register name. */
+ A64_PRINT_scalar_d_reg,
+ /* Print a scalar FP/SIMD register name. */
+ A64_PRINT_simd_q_reg,
+ /* Same for SVE registers. ('z' was already taken.) Note that it is not
+ necessary to use %Z for operands that have SVE modes. The convention is to
+ use %Z only for non-SVE (or potentially non-SVE) modes. */
+ A64_PRINT_sve_reg,
+ /* Print a FP/SIMD register name for a register list. The register printed is
+ the FP/SIMD register name of X + 0/1/2/3 for S/T/U/V. */
+ A64_PRINT_simd_reg_plus_0,
+ /* Print a FP/SIMD register name for a register list. The register printed is
+ the FP/SIMD register name of X + 0/1/2/3 for S/T/U/V. */
+ A64_PRINT_simd_reg_plus_1,
+ /* Print a FP/SIMD register name for a register list. The register printed is
+ the FP/SIMD register name of X + 0/1/2/3 for S/T/U/V. */
+ A64_PRINT_simd_reg_plus_2,
+ /* Print a FP/SIMD register name for a register list. The register printed is
+ the FP/SIMD register name of X + 0/1/2/3 for S/T/U/V. */
+ A64_PRINT_simd_reg_plus_3,
+ /* Print a scalar Integer/FP/SIMD register name + 1. */
+ A64_PRINT_scalar_intreg_plus_1,
+ /* Print bottom 16 bits of integer constant in hex. */
+ A64_PRINT_bottom_16bits,
+ /* Take the duplicated element in a vector constant and print it in hex. */
+ A64_PRINT_dup_as_hex,
+ /* Take the duplicated element in a vector constant and print it as an
+ unsigned integer, in decimal. */
+ A64_PRINT_dup_as_unsigned_dec,
+ /* Print a general register name or the zero register (32-bit or 64-bit). */
+ A64_PRINT_gpr_w,
+ /* Print a general register name or the zero register (32-bit or 64-bit). */
+ A64_PRINT_gpr_x,
+ /* Print a normal operand, if it's a general register, then we assume
+ DImode. */
+ A64_PRINT_operand,
+ /* Output address constant representing the first argument of X, specifying a
+ relocation offset if appropriate. */
+ A64_PRINT_output_addr,
+ /* Output constant address specified by X with a relocation offset if
+ appropriate. */
+ A64_PRINT_output_const_addr,
+ /* Prints address of X, specifying a PC relative relocation mode if
+ appropriate. */
+ A64_PRINT_output_addr_or_reloc,
+ /* Print NZCV for conditional compare instructions. */
+ A64_PRINT_cond_cc_code,
+ /* Print a predicate register as pn<N> rather than p<N>. */
+ A64_PRINT_pred_reg,
+ /* Output address of LDP or STP - this is used for some LDP/STPs which don't
+ use a PARALLEL in their pattern (so the mode needs to be adjusted). */
+ A64_PRINT_addr_ldp_stp_np,
+ /* Output address of a typical LDP or STP. */
+ A64_PRINT_addr_ldp_stp,
+ /* Unknown print operand. */
+ A64_PRINT_UNKNOWN
+} aarch64_print_operands_t;
+
+static hash_map<nofree_string_hash, aarch64_print_operands_t> operands_cache;
+
+struct operands_cache_initializer
+{
+ operands_cache_initializer ()
+ {
+ operands_cache.put ("A", A64_PRINT_output_addr);
+ operands_cache.put ("b", A64_PRINT_scalar_b_reg);
+ operands_cache.put ("c", A64_PRINT_constants);
+ operands_cache.put ("C", A64_PRINT_dup_as_hex);
+ operands_cache.put ("d", A64_PRINT_scalar_d_reg);
+ operands_cache.put ("D", A64_PRINT_dup_as_unsigned_dec);
+ operands_cache.put ("e", A64_PRINT_extended);
+ operands_cache.put ("p", A64_PRINT_power2n);
+ operands_cache.put ("P", A64_PRINT_num_nonzero);
+ operands_cache.put ("H", A64_PRINT_hi_regpair);
+ operands_cache.put ("I", A64_PRINT_dup_or_int);
+ operands_cache.put ("m", A64_PRINT_cc_code);
+ operands_cache.put ("M", A64_PRINT_inv_cc_code);
+ operands_cache.put ("N", A64_PRINT_neg_dup_const);
+ operands_cache.put ("h", A64_PRINT_scalar_h_reg);
+ operands_cache.put ("q", A64_PRINT_simd_q_reg);
+ operands_cache.put ("Z", A64_PRINT_sve_reg);
+ operands_cache.put ("s", A64_PRINT_scalar_s_reg);
+ operands_cache.put ("S", A64_PRINT_simd_reg_plus_0);
+ operands_cache.put ("T", A64_PRINT_simd_reg_plus_1);
+ operands_cache.put ("U", A64_PRINT_simd_reg_plus_2);
+ operands_cache.put ("V", A64_PRINT_simd_reg_plus_3);
+ operands_cache.put ("R", A64_PRINT_scalar_intreg_plus_1);
+ operands_cache.put ("w", A64_PRINT_gpr_w);
+ operands_cache.put ("L", A64_PRINT_output_const_addr);
+ operands_cache.put ("G", A64_PRINT_output_addr_or_reloc);
+ operands_cache.put ("k", A64_PRINT_cond_cc_code);
+ operands_cache.put ("K", A64_PRINT_pred_reg);
+ operands_cache.put ("x", A64_PRINT_gpr_x);
+ operands_cache.put ("X", A64_PRINT_bottom_16bits);
+ operands_cache.put ("y", A64_PRINT_addr_ldp_stp_np);
+ operands_cache.put ("z", A64_PRINT_addr_ldp_stp);
+ }
+};
+
+/* Force the static initialization. */
+static operands_cache_initializer init_operands_cache;
+
+/* Perform lookup of print operand based on current string. */
+static aarch64_print_operands_t
+aarch64_lookup_operand (const char * const *p, std::string &parsed)
+{
+ /* If p is null then it's the same as the special numerical operand 0. */
+ if (!p)
+ return A64_PRINT_operand;
+
+ /* Parse the token, multiline tokens must end in a number. */
+ if (**p == '[')
+ {
+ char v = **(++p);
+ while (v != ']')
+ {
+ parsed += v;
+ v = **(++p);
+ }
+ }
+ else
+ parsed += **p;
+
+ if (auto e = operands_cache.get (parsed.c_str ()))
+ return *e;
+
+ return A64_PRINT_UNKNOWN;
+}
+
/* Print operand X to file F in a target specific manner according to CODE.
- The acceptable formatting commands given by CODE are:
- 'c': An integer or symbol address without a preceding #
- sign.
- 'C': Take the duplicated element in a vector constant
- and print it in hex.
- 'D': Take the duplicated element in a vector constant
- and print it as an unsigned integer, in decimal.
- 'e': Print the sign/zero-extend size as a character 8->b,
- 16->h, 32->w. Can also be used for masks:
- 0xff->b, 0xffff->h, 0xffffffff->w.
- 'I': If the operand is a duplicated vector constant,
- replace it with the duplicated scalar. If the
- operand is then a floating-point constant, replace
- it with the integer bit representation. Print the
- transformed constant as a signed decimal number.
- 'p': Prints N such that 2^N == X (X must be power of 2 and
- const int).
- 'P': Print the number of non-zero bits in X (a const_int).
- 'H': Print the higher numbered register of a pair (TImode)
- of regs.
- 'm': Print a condition (eq, ne, etc).
- 'M': Same as 'm', but invert condition.
- 'N': Take the duplicated element in a vector constant
- and print the negative of it in decimal.
- 'b/h/s/d/q': Print a scalar FP/SIMD register name.
- 'Z': Same for SVE registers. ('z' was already taken.)
- Note that it is not necessary to use %Z for operands
- that have SVE modes. The convention is to use %Z
- only for non-SVE (or potentially non-SVE) modes.
- 'S/T/U/V': Print a FP/SIMD register name for a register list.
- The register printed is the FP/SIMD register name
- of X + 0/1/2/3 for S/T/U/V.
- 'R': Print a scalar Integer/FP/SIMD register name + 1.
- 'X': Print bottom 16 bits of integer constant in hex.
- 'w/x': Print a general register name or the zero register
- (32-bit or 64-bit).
- '0': Print a normal operand, if it's a general register,
- then we assume DImode.
- 'k': Print NZCV for conditional compare instructions.
- 'K': Print a predicate register as pn<N> rather than p<N>
- 'A': Output address constant representing the first
- argument of X, specifying a relocation offset
- if appropriate.
- 'L': Output constant address specified by X
- with a relocation offset if appropriate.
- 'G': Prints address of X, specifying a PC relative
- relocation mode if appropriate.
- 'y': Output address of LDP or STP - this is used for
- some LDP/STPs which don't use a PARALLEL in their
- pattern (so the mode needs to be adjusted).
- 'z': Output address of a typical LDP or STP. */
+ The acceptable formatting commands given by CODE are documented on the
+ enum aarch64_print_operands. */
static void
aarch64_print_operand (FILE *f, rtx x, const char * const *p)
{
rtx elt;
- int code = p ? (unsigned char)**p : 0;
- switch (code)
+ std::string token;
+ switch (aarch64_lookup_operand (p, token))
{
- case 'c':
+ case A64_PRINT_constants:
if (CONST_INT_P (x))
fprintf (f, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
else
@@ -13025,16 +13136,18 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
if (SYMBOL_REF_P (base))
output_addr_const (f, x);
else
- output_operand_lossage ("unsupported operand for code '%c'", code);
+ output_operand_lossage ("unsupported operand for code '%%%s'",
+ token.c_str ());
}
break;
- case 'e':
+ case A64_PRINT_extended:
{
x = unwrap_const_vec_duplicate (x);
if (!CONST_INT_P (x))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
@@ -13047,19 +13160,21 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
fputc ('w', f);
else
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
}
break;
- case 'p':
+ case A64_PRINT_power2n:
{
int n;
if (!CONST_INT_P (x) || (n = exact_log2 (INTVAL (x))) < 0)
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
@@ -13067,17 +13182,18 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
}
break;
- case 'P':
+ case A64_PRINT_num_nonzero:
if (!CONST_INT_P (x))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
asm_fprintf (f, "%u", popcount_hwi (INTVAL (x)));
break;
- case 'H':
+ case A64_PRINT_hi_regpair:
if (x == const0_rtx)
{
asm_fprintf (f, "xzr");
@@ -13086,47 +13202,50 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
if (!REG_P (x) || !GP_REGNUM_P (REGNO (x) + 1))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
asm_fprintf (f, "%s", reg_names [REGNO (x) + 1]);
break;
- case 'I':
+ case A64_PRINT_dup_or_int:
{
x = aarch64_bit_representation (unwrap_const_vec_duplicate (x));
if (CONST_INT_P (x))
asm_fprintf (f, "%wd", INTVAL (x));
else
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
break;
}
- case 'M':
- case 'm':
+ case A64_PRINT_inv_cc_code:
+ case A64_PRINT_cc_code:
{
int cond_code;
/* CONST_TRUE_RTX means al/nv (al is the default, don't print it). */
if (x == const_true_rtx)
{
- if (code == 'M')
+ if (token == "M")
fputs ("nv", f);
return;
}
if (!COMPARISON_P (x))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
cond_code = aarch64_get_condition_code (x);
gcc_assert (cond_code >= 0);
- if (code == 'M')
+ if (token == "M")
cond_code = AARCH64_INVERSE_CONDITION_CODE (cond_code);
if (GET_MODE (XEXP (x, 0)) == CC_NZCmode)
fputs (aarch64_sve_condition_codes[cond_code], f);
@@ -13135,7 +13254,7 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
}
break;
- case 'N':
+ case A64_PRINT_neg_dup_const:
if (!const_vec_duplicate_p (x, &elt))
{
output_operand_lossage ("invalid vector constant");
@@ -13154,39 +13273,46 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
}
break;
- case 'b':
- case 'h':
- case 's':
- case 'd':
- case 'q':
- case 'Z':
- code = TOLOWER (code);
- if (!REG_P (x) || !FP_REGNUM_P (REGNO (x)))
- {
- output_operand_lossage ("incompatible floating point / vector register operand for '%%%c'", code);
- return;
- }
- asm_fprintf (f, "%c%d", code, REGNO (x) - V0_REGNUM);
+ case A64_PRINT_scalar_b_reg:
+ case A64_PRINT_scalar_h_reg:
+ case A64_PRINT_scalar_s_reg:
+ case A64_PRINT_scalar_d_reg:
+ case A64_PRINT_simd_q_reg:
+ case A64_PRINT_sve_reg:
+ {
+ int code = TOLOWER (token[0]);
+ if (!REG_P (x) || !FP_REGNUM_P (REGNO (x)))
+ {
+ output_operand_lossage ("incompatible floating point / vector register operand for '%%%s'",
+ token.c_str ());
+ return;
+ }
+ asm_fprintf (f, "%c%d", code, REGNO (x) - V0_REGNUM);
+ }
break;
- case 'S':
- case 'T':
- case 'U':
- case 'V':
- if (!REG_P (x) || (!FP_REGNUM_P (REGNO (x)) && !PR_REGNUM_P (REGNO (x))))
- {
- output_operand_lossage ("incompatible operand for '%%%c'", code);
- return;
- }
- if (PR_REGNUM_P (REGNO (x)))
- asm_fprintf (f, "p%d", REGNO (x) - P0_REGNUM + (code - 'S'));
- else
- asm_fprintf (f, "%c%d",
- aarch64_sve_data_mode_p (GET_MODE (x)) ? 'z' : 'v',
- REGNO (x) - V0_REGNUM + (code - 'S'));
+ case A64_PRINT_simd_reg_plus_0:
+ case A64_PRINT_simd_reg_plus_1:
+ case A64_PRINT_simd_reg_plus_2:
+ case A64_PRINT_simd_reg_plus_3:
+ {
+ int code = token[0];
+ if (!REG_P (x)
+ || (!FP_REGNUM_P (REGNO (x)) && !PR_REGNUM_P (REGNO (x))))
+ {
+ output_operand_lossage ("incompatible operand for '%%%c'", code);
+ return;
+ }
+ if (PR_REGNUM_P (REGNO (x)))
+ asm_fprintf (f, "p%d", REGNO (x) - P0_REGNUM + (code - 'S'));
+ else
+ asm_fprintf (f, "%c%d",
+ aarch64_sve_data_mode_p (GET_MODE (x)) ? 'z' : 'v',
+ REGNO (x) - V0_REGNUM + (code - 'S'));
+ }
break;
- case 'R':
+ case A64_PRINT_scalar_intreg_plus_1:
if (REG_P (x) && FP_REGNUM_P (REGNO (x))
&& (aarch64_advsimd_partial_struct_mode_p (GET_MODE (x))))
asm_fprintf (f, "d%d", REGNO (x) - V0_REGNUM + 1);
@@ -13195,25 +13321,26 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
else if (REG_P (x) && GP_REGNUM_P (REGNO (x)))
asm_fprintf (f, "x%d", REGNO (x) - R0_REGNUM + 1);
else
- output_operand_lossage ("incompatible register operand for '%%%c'",
- code);
+ output_operand_lossage ("incompatible register operand for '%%%s'",
+ token.c_str ());
break;
- case 'X':
+ case A64_PRINT_bottom_16bits:
if (!CONST_INT_P (x))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'", token.c_str ());
return;
}
asm_fprintf (f, "0x%wx", UINTVAL (x) & 0xffff);
break;
- case 'C':
+ case A64_PRINT_dup_as_hex:
{
/* Print a replicated constant in hex. */
if (!const_vec_duplicate_p (x, &elt) || !CONST_INT_P (elt))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
scalar_mode inner_mode = GET_MODE_INNER (GET_MODE (x));
@@ -13221,13 +13348,14 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
}
break;
- case 'D':
+ case A64_PRINT_dup_as_unsigned_dec:
{
/* Print a replicated constant in decimal, treating it as
unsigned. */
if (!const_vec_duplicate_p (x, &elt) || !CONST_INT_P (elt))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
scalar_mode inner_mode = GET_MODE_INNER (GET_MODE (x));
@@ -13235,29 +13363,29 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
}
break;
- case 'w':
- case 'x':
+ case A64_PRINT_gpr_w:
+ case A64_PRINT_gpr_x:
if (aarch64_const_zero_rtx_p (x))
{
- asm_fprintf (f, "%czr", code);
+ asm_fprintf (f, "%czr", token[0]);
break;
}
if (REG_P (x) && GP_REGNUM_P (REGNO (x)))
{
- asm_fprintf (f, "%c%d", code, REGNO (x) - R0_REGNUM);
+ asm_fprintf (f, "%c%d", token[0], REGNO (x) - R0_REGNUM);
break;
}
if (REG_P (x) && REGNO (x) == SP_REGNUM)
{
- asm_fprintf (f, "%ssp", code == 'w' ? "w" : "");
+ asm_fprintf (f, "%ssp", token == "w" ? "w" : "");
break;
}
/* Fall through */
- case 0:
+ case A64_PRINT_operand:
if (x == NULL)
{
output_operand_lossage ("missing operand");
@@ -13358,7 +13486,7 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
}
break;
- case 'A':
+ case A64_PRINT_output_addr:
if (GET_CODE (x) == HIGH)
x = XEXP (x, 0);
@@ -13394,7 +13522,7 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
output_addr_const (asm_out_file, x);
break;
- case 'L':
+ case A64_PRINT_output_const_addr:
switch (aarch64_classify_symbolic_expression (x))
{
case SYMBOL_SMALL_GOT_4G:
@@ -13435,7 +13563,7 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
output_addr_const (asm_out_file, x);
break;
- case 'G':
+ case A64_PRINT_output_addr_or_reloc:
switch (aarch64_classify_symbolic_expression (x))
{
case SYMBOL_TLSLE24:
@@ -13447,13 +13575,14 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
output_addr_const (asm_out_file, x);
break;
- case 'k':
+ case A64_PRINT_cond_cc_code:
{
HOST_WIDE_INT cond_code;
if (!CONST_INT_P (x))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
@@ -13463,40 +13592,43 @@ aarch64_print_operand (FILE *f, rtx x, const char * const *p)
}
break;
- case 'K':
+ case A64_PRINT_pred_reg:
if (!REG_P (x) || !PR_REGNUM_P (REGNO (x)))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'", token.c_str ());
return;
}
asm_fprintf (f, "pn%d", REGNO (x) - P0_REGNUM);
break;
- case 'y':
- case 'z':
+ case A64_PRINT_addr_ldp_stp_np:
+ case A64_PRINT_addr_ldp_stp:
{
machine_mode mode = GET_MODE (x);
if (!MEM_P (x)
- || (code == 'y'
+ || (token == "y"
&& maybe_ne (GET_MODE_SIZE (mode), 8)
&& maybe_ne (GET_MODE_SIZE (mode), 16)
&& maybe_ne (GET_MODE_SIZE (mode), 32)))
{
- output_operand_lossage ("invalid operand for '%%%c'", code);
+ output_operand_lossage ("invalid operand for '%%%s'",
+ token.c_str ());
return;
}
if (!aarch64_print_address_internal (f, mode, XEXP (x, 0),
- code == 'y'
+ token == "y"
? ADDR_QUERY_LDP_STP_N
: ADDR_QUERY_LDP_STP))
- output_operand_lossage ("invalid operand prefix '%%%c'", code);
+ output_operand_lossage ("invalid operand prefix '%%%s'",
+ token.c_str ());
}
break;
default:
- output_operand_lossage ("invalid operand prefix '%%%c'", code);
+ output_operand_lossage ("invalid operand prefix '%%%s'",
+ token.c_str ());
return;
}
}