From 32d715691aa037f2838b41fa257c2e239d67c134 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Mon, 12 Apr 2021 09:02:46 +0930 Subject: Power10 bignum operands When built on a 32-bit host without --enable-64-bit-bfd, powerpc-linux and other 32-bit powerpc targeted binutils fail to assemble some power10 prefixed instructions with 34-bit fields. A typical error seen when running the testsuite is .../gas/testsuite/gas/ppc/prefix-pcrel.s:10: Error: bignum invalid In practice this doesn't matter for addresses: 32-bit programs don't need or use the top 2 bits of a d34 field when calculating addresses. However it may matter when loading or adding 64-bit constants with paddi. A power10 processor in 32-bit mode still has 64-bit wide GPRs. So this patch enables limited support for O_big PowerPC operands, and corrects sign extension of 32-bit constants using X_extrabit. * config/tc-ppc.c (insn_validate): Use uint64_t for operand values. (md_assemble): Likewise. Handle bignum operands. (ppc_elf_suffix): Handle O_big. Remove unnecessary input_line_pointer check. * expr.c: Delete unnecessary forward declarations. (generic_bignum_to_int32): Return uint32_t. (generic_bignum_to_int64): Return uint64_t. Compile always. (operand): Twiddle X_extrabit for unary '~'. Set X_unsigned and clear X_extrabit for unary '!'. * expr.h (generic_bignum_to_int32): Declare. (generic_bignum_to_int64): Declare. * testsuite/gas/ppc/prefix-pcrel.s, * testsuite/gas/ppc/prefix-pcrel.d: Add more instructions. --- gas/config/tc-ppc.c | 84 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 36 deletions(-) (limited to 'gas/config/tc-ppc.c') diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c index 5511e72..c719b40 100644 --- a/gas/config/tc-ppc.c +++ b/gas/config/tc-ppc.c @@ -1574,7 +1574,7 @@ insn_validate (const struct powerpc_opcode *op) if (operand->shift == (int) PPC_OPSHIFT_INV) { const char *errmsg; - int64_t val; + uint64_t val; errmsg = NULL; val = -1; @@ -2197,7 +2197,7 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p) { int reloc = ptr->reloc; - if (!ppc_obj64 && exp_p->X_add_number != 0) + if (!ppc_obj64 && (exp_p->X_op == O_big || exp_p->X_add_number != 0)) { switch (reloc) { @@ -2238,14 +2238,12 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p) input_line_pointer = str; expression (&new_exp); - if (new_exp.X_op == O_constant) + if (new_exp.X_op == O_constant && exp_p->X_op != O_big) { exp_p->X_add_number += new_exp.X_add_number; str = input_line_pointer; } - - if (&input_line_pointer != str_p) - input_line_pointer = orig_line; + input_line_pointer = orig_line; } *str_p = str; @@ -3397,8 +3395,8 @@ md_assemble (char *str) } if (--num_optional_provided < 0) { - int64_t val = ppc_optional_operand_value (operand, insn, ppc_cpu, - num_optional_provided); + uint64_t val = ppc_optional_operand_value (operand, insn, ppc_cpu, + num_optional_provided); if (operand->insert) { insn = (*operand->insert) (insn, val, ppc_cpu, &errmsg); @@ -3459,14 +3457,32 @@ md_assemble (char *str) insn = ppc_insert_operand (insn, operand, ex.X_add_number, ppc_cpu, (char *) NULL, 0); } - else if (ex.X_op == O_constant) + else if (ex.X_op == O_constant + || (ex.X_op == O_big && ex.X_add_number > 0)) { + uint64_t val; + if (ex.X_op == O_constant) + { + val = ex.X_add_number; + if (sizeof (ex.X_add_number) < sizeof (val) + && (ex.X_add_number < 0) != ex.X_extrabit) + val = val ^ ((addressT) -1 ^ (uint64_t) -1); + } + else + val = generic_bignum_to_int64 (); #ifdef OBJ_ELF /* Allow @HA, @L, @H on constants. */ - bfd_reloc_code_real_type reloc; char *orig_str = str; + bfd_reloc_code_real_type reloc = ppc_elf_suffix (&str, &ex); - if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE) + if (ex.X_op == O_constant) + { + val = ex.X_add_number; + if (sizeof (ex.X_add_number) < sizeof (val) + && (ex.X_add_number < 0) != ex.X_extrabit) + val = val ^ ((addressT) -1 ^ (uint64_t) -1); + } + if (reloc != BFD_RELOC_NONE) switch (reloc) { default: @@ -3474,81 +3490,77 @@ md_assemble (char *str) break; case BFD_RELOC_LO16: - ex.X_add_number &= 0xffff; + val &= 0xffff; if ((operand->flags & PPC_OPERAND_SIGNED) != 0) - ex.X_add_number = SEX16 (ex.X_add_number); + val = SEX16 (val); break; case BFD_RELOC_HI16: if (REPORT_OVERFLOW_HI && ppc_obj64) { /* PowerPC64 @h is tested for overflow. */ - ex.X_add_number = (addressT) ex.X_add_number >> 16; + val = val >> 16; if ((operand->flags & PPC_OPERAND_SIGNED) != 0) { - addressT sign = (((addressT) -1 >> 16) + 1) >> 1; - ex.X_add_number - = ((addressT) ex.X_add_number ^ sign) - sign; + uint64_t sign = (((uint64_t) -1 >> 16) + 1) >> 1; + val = (val ^ sign) - sign; } break; } /* Fallthru */ case BFD_RELOC_PPC64_ADDR16_HIGH: - ex.X_add_number = PPC_HI (ex.X_add_number); + val = PPC_HI (val); if ((operand->flags & PPC_OPERAND_SIGNED) != 0) - ex.X_add_number = SEX16 (ex.X_add_number); + val = SEX16 (val); break; case BFD_RELOC_HI16_S: if (REPORT_OVERFLOW_HI && ppc_obj64) { /* PowerPC64 @ha is tested for overflow. */ - ex.X_add_number - = ((addressT) ex.X_add_number + 0x8000) >> 16; + val = (val + 0x8000) >> 16; if ((operand->flags & PPC_OPERAND_SIGNED) != 0) { - addressT sign = (((addressT) -1 >> 16) + 1) >> 1; - ex.X_add_number - = ((addressT) ex.X_add_number ^ sign) - sign; + uint64_t sign = (((uint64_t) -1 >> 16) + 1) >> 1; + val = (val ^ sign) - sign; } break; } /* Fallthru */ case BFD_RELOC_PPC64_ADDR16_HIGHA: - ex.X_add_number = PPC_HA (ex.X_add_number); + val = PPC_HA (val); if ((operand->flags & PPC_OPERAND_SIGNED) != 0) - ex.X_add_number = SEX16 (ex.X_add_number); + val = SEX16 (val); break; case BFD_RELOC_PPC64_HIGHER: - ex.X_add_number = PPC_HIGHER (ex.X_add_number); + val = PPC_HIGHER (val); if ((operand->flags & PPC_OPERAND_SIGNED) != 0) - ex.X_add_number = SEX16 (ex.X_add_number); + val = SEX16 (val); break; case BFD_RELOC_PPC64_HIGHER_S: - ex.X_add_number = PPC_HIGHERA (ex.X_add_number); + val = PPC_HIGHERA (val); if ((operand->flags & PPC_OPERAND_SIGNED) != 0) - ex.X_add_number = SEX16 (ex.X_add_number); + val = SEX16 (val); break; case BFD_RELOC_PPC64_HIGHEST: - ex.X_add_number = PPC_HIGHEST (ex.X_add_number); + val = PPC_HIGHEST (val); if ((operand->flags & PPC_OPERAND_SIGNED) != 0) - ex.X_add_number = SEX16 (ex.X_add_number); + val = SEX16 (val); break; case BFD_RELOC_PPC64_HIGHEST_S: - ex.X_add_number = PPC_HIGHESTA (ex.X_add_number); + val = PPC_HIGHESTA (val); if ((operand->flags & PPC_OPERAND_SIGNED) != 0) - ex.X_add_number = SEX16 (ex.X_add_number); + val = SEX16 (val); break; } #endif /* OBJ_ELF */ - insn = ppc_insert_operand (insn, operand, ex.X_add_number, - ppc_cpu, (char *) NULL, 0); + insn = ppc_insert_operand (insn, operand, val, ppc_cpu, NULL, 0); } else { -- cgit v1.1