diff options
author | Andre Vieira <andre.simoesdiasvieira@arm.com> | 2019-05-15 17:20:46 +0100 |
---|---|---|
committer | Andre Vieira <andre.simoesdiasvieira@arm.com> | 2019-05-16 16:22:09 +0100 |
commit | f5f10c66f8dc5466536181a1e5cce2419a5bcbd7 (patch) | |
tree | 89eb436c9a481c9e8f36f6479541431b731060ba /gas/config/tc-arm.c | |
parent | 35c228db7089caf9525c1ef4cb35f6a8335eeea9 (diff) | |
download | gdb-f5f10c66f8dc5466536181a1e5cce2419a5bcbd7.zip gdb-f5f10c66f8dc5466536181a1e5cce2419a5bcbd7.tar.gz gdb-f5f10c66f8dc5466536181a1e5cce2419a5bcbd7.tar.bz2 |
[PATCH 7/57][Arm][GAS] Add support for MVE instructions: vstr/vldr
gas/ChangeLog:
2019-05-16 Andre Vieira <andre.simoesdiasvieira@arm.com>
* config/tc-arm.c (struct arm_it): Make immisreg field larger to hold
type of register.
(enum shift_kind): Add SHIFT_UXTW shift kind.
(enum parse_shift_mode): Add SHIFT_UXTW_IMMEDIATE shift mode.
(parse_shift): Handle new shift type.
(parse_address_main): Accept new addressing modes.
(M_MNEM_vstrb, M_MNEM_vstrh, M_MNEM_vstrw, M_MNEM_vstrd,
M_MNEM_vldrb, M_MNEM_vldrh, M_MNEM_vldrw, M_MNEM_vldrd): New
instruction encodings.
(do_mve_vstr_vldr_QI): New encoding functions.
(do_mve_vstr_vldr_RQ): Likewise.
(do_mve_vstr_vldr_RI): Likewise.
(do_mve_vstr_vldr): Likewise.
* testsuite/gas/arm/mve-vldr-bad-1.d: New test.
* testsuite/gas/arm/mve-vldr-bad-1.l: New test.
* testsuite/gas/arm/mve-vldr-bad-1.s: New test.
* testsuite/gas/arm/mve-vldr-bad-2.d: New test.
* testsuite/gas/arm/mve-vldr-bad-2.l: New test.
* testsuite/gas/arm/mve-vldr-bad-2.s: New test.
* testsuite/gas/arm/mve-vldr-bad-3.d: New test.
* testsuite/gas/arm/mve-vldr-bad-3.l: New test.
* testsuite/gas/arm/mve-vldr-bad-3.s: New test.
* testsuite/gas/arm/mve-vstr-bad-1.d: New test.
* testsuite/gas/arm/mve-vstr-bad-1.l: New test.
* testsuite/gas/arm/mve-vstr-bad-1.s: New test.
* testsuite/gas/arm/mve-vstr-bad-2.d: New test.
* testsuite/gas/arm/mve-vstr-bad-2.l: New test.
* testsuite/gas/arm/mve-vstr-bad-2.s: New test.
* testsuite/gas/arm/mve-vstr-bad-3.d: New test.
* testsuite/gas/arm/mve-vstr-bad-3.l: New test.
* testsuite/gas/arm/mve-vstr-bad-3.s: New test.
Diffstat (limited to 'gas/config/tc-arm.c')
-rw-r--r-- | gas/config/tc-arm.c | 328 |
1 files changed, 321 insertions, 7 deletions
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index d2f04d7..717f1b1 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -508,7 +508,8 @@ struct arm_it struct neon_type_el vectype; unsigned present : 1; /* Operand present. */ unsigned isreg : 1; /* Operand was a register. */ - unsigned immisreg : 1; /* .imm field is a second register. */ + unsigned immisreg : 2; /* .imm field is a second register. + 0: imm, 1: gpr, 2: MVE Q-register. */ unsigned isscalar : 1; /* Operand is a (Neon) scalar. */ unsigned immisalign : 1; /* Immediate is an alignment specifier. */ unsigned immisfloat : 1; /* Immediate was parsed as a float. */ @@ -5289,7 +5290,7 @@ parse_qfloat_immediate (char **ccp, int *immed) /* Shift operands. */ enum shift_kind { - SHIFT_LSL, SHIFT_LSR, SHIFT_ASR, SHIFT_ROR, SHIFT_RRX + SHIFT_LSL, SHIFT_LSR, SHIFT_ASR, SHIFT_ROR, SHIFT_RRX, SHIFT_UXTW }; struct asm_shift_name @@ -5306,6 +5307,7 @@ enum parse_shift_mode SHIFT_LSL_OR_ASR_IMMEDIATE, /* Shift must be LSL or ASR immediate. */ SHIFT_ASR_IMMEDIATE, /* Shift must be ASR immediate. */ SHIFT_LSL_IMMEDIATE, /* Shift must be LSL immediate. */ + SHIFT_UXTW_IMMEDIATE /* Shift must be UXTW immediate. */ }; /* Parse a <shift> specifier on an ARM data processing instruction. @@ -5350,7 +5352,13 @@ parse_shift (char **str, int i, enum parse_shift_mode mode) switch (mode) { case NO_SHIFT_RESTRICT: - case SHIFT_IMMEDIATE: break; + case SHIFT_IMMEDIATE: + if (shift == SHIFT_UXTW) + { + inst.error = _("'UXTW' not allowed here"); + return FAIL; + } + break; case SHIFT_LSL_OR_ASR_IMMEDIATE: if (shift != SHIFT_LSL && shift != SHIFT_ASR) @@ -5375,6 +5383,13 @@ parse_shift (char **str, int i, enum parse_shift_mode mode) return FAIL; } break; + case SHIFT_UXTW_IMMEDIATE: + if (shift != SHIFT_UXTW) + { + inst.error = _("'UXTW' required"); + return FAIL; + } + break; default: abort (); } @@ -5744,7 +5759,21 @@ parse_address_main (char **str, int i, int group_relocations, /* PR gas/14887: Allow for whitespace after the opening bracket. */ skip_whitespace (p); - if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL) + if (group_type == GROUP_MVE) + { + enum arm_reg_type rtype = REG_TYPE_MQ; + struct neon_type_el et; + if ((reg = arm_typed_reg_parse (&p, rtype, &rtype, &et)) != FAIL) + { + inst.operands[i].isquad = 1; + } + else if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL) + { + inst.error = BAD_ADDR_MODE; + return PARSE_OPERAND_FAIL; + } + } + else if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL) { if (group_type == GROUP_MVE) inst.error = BAD_ADDR_MODE; @@ -5762,7 +5791,26 @@ parse_address_main (char **str, int i, int group_relocations, if (*p == '+') p++; else if (*p == '-') p++, inst.operands[i].negative = 1; - if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL) + enum arm_reg_type rtype = REG_TYPE_MQ; + struct neon_type_el et; + if (group_type == GROUP_MVE + && (reg = arm_typed_reg_parse (&p, rtype, &rtype, &et)) != FAIL) + { + inst.operands[i].immisreg = 2; + inst.operands[i].imm = reg; + + if (skip_past_comma (&p) == SUCCESS) + { + if (parse_shift (&p, i, SHIFT_UXTW_IMMEDIATE) == SUCCESS) + { + inst.operands[i].imm |= inst.relocs[0].exp.X_add_number << 5; + inst.relocs[0].exp.X_add_number = 0; + } + else + return PARSE_OPERAND_FAIL; + } + } + else if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL) { inst.operands[i].imm = reg; inst.operands[i].immisreg = 1; @@ -5919,7 +5967,15 @@ parse_address_main (char **str, int i, int group_relocations, if (*p == '+') p++; else if (*p == '-') p++, inst.operands[i].negative = 1; - if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL) + enum arm_reg_type rtype = REG_TYPE_MQ; + struct neon_type_el et; + if (group_type == GROUP_MVE + && (reg = arm_typed_reg_parse (&p, rtype, &rtype, &et)) != FAIL) + { + inst.operands[i].immisreg = 2; + inst.operands[i].imm = reg; + } + else if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL) { /* We might be using the immediate for alignment already. If we are, OR the register number into the low-order bits. */ @@ -13893,6 +13949,14 @@ do_t_loloop (void) #define M_MNEM_vld41 0xfc901e21 #define M_MNEM_vld42 0xfc901e41 #define M_MNEM_vld43 0xfc901e61 +#define M_MNEM_vstrb 0xec000e00 +#define M_MNEM_vstrh 0xec000e10 +#define M_MNEM_vstrw 0xec000e40 +#define M_MNEM_vstrd 0xec000e50 +#define M_MNEM_vldrb 0xec100e00 +#define M_MNEM_vldrh 0xec100e10 +#define M_MNEM_vldrw 0xec100e40 +#define M_MNEM_vldrd 0xec100e50 /* Neon instruction encoder helpers. */ @@ -15741,6 +15805,247 @@ check_simd_pred_availability (int fp, unsigned check) } static void +do_mve_vstr_vldr_QI (int size, int elsize, int load) +{ + constraint (size < 32, BAD_ADDR_MODE); + constraint (size != elsize, BAD_EL_TYPE); + constraint (inst.operands[1].immisreg, BAD_ADDR_MODE); + constraint (!inst.operands[1].preind, BAD_ADDR_MODE); + constraint (load && inst.operands[0].reg == inst.operands[1].reg, + _("destination register and offset register may not be the" + " same")); + + int imm = inst.relocs[0].exp.X_add_number; + int add = 1; + if (imm < 0) + { + add = 0; + imm = -imm; + } + constraint ((imm % (size / 8) != 0) + || imm > (0x7f << neon_logbits (size)), + (size == 32) ? _("immediate must be a multiple of 4 in the" + " range of +/-[0,508]") + : _("immediate must be a multiple of 8 in the" + " range of +/-[0,1016]")); + inst.instruction |= 0x11 << 24; + inst.instruction |= add << 23; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= inst.operands[1].writeback << 21; + inst.instruction |= LOW4 (inst.operands[1].reg) << 16; + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= 1 << 12; + inst.instruction |= (size == 64) << 8; + inst.instruction &= 0xffffff00; + inst.instruction |= HI1 (inst.operands[1].reg) << 7; + inst.instruction |= imm >> neon_logbits (size); +} + +static void +do_mve_vstr_vldr_RQ (int size, int elsize, int load) +{ + unsigned os = inst.operands[1].imm >> 5; + constraint (os != 0 && size == 8, + _("can not shift offsets when accessing less than half-word")); + constraint (os && os != neon_logbits (size), + _("shift immediate must be 1, 2 or 3 for half-word, word" + " or double-word accesses respectively")); + if (inst.operands[1].reg == REG_PC) + as_tsktsk (MVE_BAD_PC); + + switch (size) + { + case 8: + constraint (elsize >= 64, BAD_EL_TYPE); + break; + case 16: + constraint (elsize < 16 || elsize >= 64, BAD_EL_TYPE); + break; + case 32: + case 64: + constraint (elsize != size, BAD_EL_TYPE); + break; + default: + break; + } + constraint (inst.operands[1].writeback || !inst.operands[1].preind, + BAD_ADDR_MODE); + if (load) + { + constraint (inst.operands[0].reg == (inst.operands[1].imm & 0x1f), + _("destination register and offset register may not be" + " the same")); + constraint (size == elsize && inst.vectype.el[0].type != NT_unsigned, + BAD_EL_TYPE); + constraint (inst.vectype.el[0].type != NT_unsigned + && inst.vectype.el[0].type != NT_signed, BAD_EL_TYPE); + inst.instruction |= (inst.vectype.el[0].type == NT_unsigned) << 28; + } + else + { + constraint (inst.vectype.el[0].type != NT_untyped, BAD_EL_TYPE); + } + + inst.instruction |= 1 << 23; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= inst.operands[1].reg << 16; + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction |= neon_logbits (elsize) << 7; + inst.instruction |= HI1 (inst.operands[1].imm) << 5; + inst.instruction |= LOW4 (inst.operands[1].imm); + inst.instruction |= !!os; +} + +static void +do_mve_vstr_vldr_RI (int size, int elsize, int load) +{ + enum neon_el_type type = inst.vectype.el[0].type; + + constraint (size >= 64, BAD_ADDR_MODE); + switch (size) + { + case 16: + constraint (elsize < 16 || elsize >= 64, BAD_EL_TYPE); + break; + case 32: + constraint (elsize != size, BAD_EL_TYPE); + break; + default: + break; + } + if (load) + { + constraint (elsize != size && type != NT_unsigned + && type != NT_signed, BAD_EL_TYPE); + } + else + { + constraint (elsize != size && type != NT_untyped, BAD_EL_TYPE); + } + + int imm = inst.relocs[0].exp.X_add_number; + int add = 1; + if (imm < 0) + { + add = 0; + imm = -imm; + } + + if ((imm % (size / 8) != 0) || imm > (0x7f << neon_logbits (size))) + { + switch (size) + { + case 8: + constraint (1, _("immediate must be in the range of +/-[0,127]")); + break; + case 16: + constraint (1, _("immediate must be a multiple of 2 in the" + " range of +/-[0,254]")); + break; + case 32: + constraint (1, _("immediate must be a multiple of 4 in the" + " range of +/-[0,508]")); + break; + } + } + + if (size != elsize) + { + constraint (inst.operands[1].reg > 7, BAD_HIREG); + constraint (inst.operands[0].reg > 14, + _("MVE vector register in the range [Q0..Q7] expected")); + inst.instruction |= (load && type == NT_unsigned) << 28; + inst.instruction |= (size == 16) << 19; + inst.instruction |= neon_logbits (elsize) << 7; + } + else + { + if (inst.operands[1].reg == REG_PC) + as_tsktsk (MVE_BAD_PC); + else if (inst.operands[1].reg == REG_SP && inst.operands[1].writeback) + as_tsktsk (MVE_BAD_SP); + inst.instruction |= 1 << 12; + inst.instruction |= neon_logbits (size) << 7; + } + inst.instruction |= inst.operands[1].preind << 24; + inst.instruction |= add << 23; + inst.instruction |= HI1 (inst.operands[0].reg) << 22; + inst.instruction |= inst.operands[1].writeback << 21; + inst.instruction |= inst.operands[1].reg << 16; + inst.instruction |= LOW4 (inst.operands[0].reg) << 12; + inst.instruction &= 0xffffff80; + inst.instruction |= imm >> neon_logbits (size); + +} + +static void +do_mve_vstr_vldr (void) +{ + unsigned size; + int load = 0; + + if (inst.cond > COND_ALWAYS) + inst.pred_insn_type = INSIDE_VPT_INSN; + else + inst.pred_insn_type = MVE_OUTSIDE_PRED_INSN; + + switch (inst.instruction) + { + default: + gas_assert (0); + break; + case M_MNEM_vldrb: + load = 1; + /* fall through. */ + case M_MNEM_vstrb: + size = 8; + break; + case M_MNEM_vldrh: + load = 1; + /* fall through. */ + case M_MNEM_vstrh: + size = 16; + break; + case M_MNEM_vldrw: + load = 1; + /* fall through. */ + case M_MNEM_vstrw: + size = 32; + break; + case M_MNEM_vldrd: + load = 1; + /* fall through. */ + case M_MNEM_vstrd: + size = 64; + break; + } + unsigned elsize = inst.vectype.el[0].size; + + if (inst.operands[1].isquad) + { + /* We are dealing with [Q, imm]{!} cases. */ + do_mve_vstr_vldr_QI (size, elsize, load); + } + else + { + if (inst.operands[1].immisreg == 2) + { + /* We are dealing with [R, Q, {UXTW #os}] cases. */ + do_mve_vstr_vldr_RQ (size, elsize, load); + } + else if (!inst.operands[1].immisreg) + { + /* We are dealing with [R, Imm]{!}/[R], Imm cases. */ + do_mve_vstr_vldr_RI (size, elsize, load); + } + else + constraint (1, BAD_ADDR_MODE); + } + + inst.is_neon = 1; +} + +static void do_mve_vst_vld (void) { if (!ARM_CPU_HAS_FEATURE (cpu_variant, mve_ext)) @@ -20578,7 +20883,8 @@ static const struct asm_shift_name shift_names [] = { "lsr", SHIFT_LSR }, { "LSR", SHIFT_LSR }, { "asr", SHIFT_ASR }, { "ASR", SHIFT_ASR }, { "ror", SHIFT_ROR }, { "ROR", SHIFT_ROR }, - { "rrx", SHIFT_RRX }, { "RRX", SHIFT_RRX } + { "rrx", SHIFT_RRX }, { "RRX", SHIFT_RRX }, + { "uxtw", SHIFT_UXTW}, { "UXTW", SHIFT_UXTW} }; /* Table of all explicit relocation names. */ @@ -22948,6 +23254,14 @@ static const struct asm_opcode insns[] = mCEF(vld41, _vld41, 2, (MSTRLST4, ADDRMVE), mve_vst_vld), mCEF(vld42, _vld42, 2, (MSTRLST4, ADDRMVE), mve_vst_vld), mCEF(vld43, _vld43, 2, (MSTRLST4, ADDRMVE), mve_vst_vld), + mCEF(vstrb, _vstrb, 2, (RMQ, ADDRMVE), mve_vstr_vldr), + mCEF(vstrh, _vstrh, 2, (RMQ, ADDRMVE), mve_vstr_vldr), + mCEF(vstrw, _vstrw, 2, (RMQ, ADDRMVE), mve_vstr_vldr), + mCEF(vstrd, _vstrd, 2, (RMQ, ADDRMVE), mve_vstr_vldr), + mCEF(vldrb, _vldrb, 2, (RMQ, ADDRMVE), mve_vstr_vldr), + mCEF(vldrh, _vldrh, 2, (RMQ, ADDRMVE), mve_vstr_vldr), + mCEF(vldrw, _vldrw, 2, (RMQ, ADDRMVE), mve_vstr_vldr), + mCEF(vldrd, _vldrd, 2, (RMQ, ADDRMVE), mve_vstr_vldr), #undef ARM_VARIANT #define ARM_VARIANT & fpu_vfp_ext_v1xd |