+/* Helper for get_dwarf2_rational_constant that computes the value of
+ a given gmp_mpz given an attribute. */
+
+static void
+get_mpz (struct dwarf2_cu *cu, gdb_mpz *value, struct attribute *attr)
+{
+ /* GCC will sometimes emit a 16-byte constant value as a DWARF
+ location expression that pushes an implicit value. */
+ if (attr->form == DW_FORM_exprloc)
+ {
+ dwarf_block *blk = attr->as_block ();
+ if (blk->size > 0 && blk->data[0] == DW_OP_implicit_value)
+ {
+ uint64_t len;
+ const gdb_byte *ptr = safe_read_uleb128 (blk->data + 1,
+ blk->data + blk->size,
+ &len);
+ if (ptr - blk->data + len <= blk->size)
+ {
+ mpz_import (value->val, len,
+ bfd_big_endian (cu->per_objfile->objfile->obfd) ? 1 : -1,
+ 1, 0, 0, ptr);
+ return;
+ }
+ }
+
+ /* On failure set it to 1. */
+ *value = gdb_mpz (1);
+ }
+ else if (attr->form_is_block ())
+ {
+ dwarf_block *blk = attr->as_block ();
+ mpz_import (value->val, blk->size,
+ bfd_big_endian (cu->per_objfile->objfile->obfd) ? 1 : -1,
+ 1, 0, 0, blk->data);
+ }
+ else
+ *value = gdb_mpz (attr->constant_value (1));
+}
+
+/* Assuming DIE is a rational DW_TAG_constant, read the DIE's
+ numerator and denominator into NUMERATOR and DENOMINATOR (resp).
+
+ If the numerator and/or numerator attribute is missing,
+ a complaint is filed, and NUMERATOR and DENOMINATOR are left
+ untouched. */
+
+static void
+get_dwarf2_rational_constant (struct die_info *die, struct dwarf2_cu *cu,
+ gdb_mpz *numerator, gdb_mpz *denominator)
+{
+ struct attribute *num_attr, *denom_attr;
+
+ num_attr = dwarf2_attr (die, DW_AT_GNU_numerator, cu);
+ if (num_attr == nullptr)
+ complaint (_("DW_AT_GNU_numerator missing in %s DIE at %s"),
+ dwarf_tag_name (die->tag), sect_offset_str (die->sect_off));
+
+ denom_attr = dwarf2_attr (die, DW_AT_GNU_denominator, cu);
+ if (denom_attr == nullptr)
+ complaint (_("DW_AT_GNU_denominator missing in %s DIE at %s"),
+ dwarf_tag_name (die->tag), sect_offset_str (die->sect_off));
+
+ if (num_attr == nullptr || denom_attr == nullptr)
+ return;
+
+ get_mpz (cu, numerator, num_attr);
+ get_mpz (cu, denominator, denom_attr);
+}
+
+/* Same as get_dwarf2_rational_constant, but extracting an unsigned
+ rational constant, rather than a signed one.
+
+ If the rational constant has a negative value, a complaint
+ is filed, and NUMERATOR and DENOMINATOR are left untouched. */
+
+static void
+get_dwarf2_unsigned_rational_constant (struct die_info *die,
+ struct dwarf2_cu *cu,
+ gdb_mpz *numerator,
+ gdb_mpz *denominator)
+{
+ gdb_mpz num (1);
+ gdb_mpz denom (1);
+
+ get_dwarf2_rational_constant (die, cu, &num, &denom);
+ if (mpz_sgn (num.val) == -1 && mpz_sgn (denom.val) == -1)
+ {
+ mpz_neg (num.val, num.val);
+ mpz_neg (denom.val, denom.val);
+ }
+ else if (mpz_sgn (num.val) == -1)
+ {
+ complaint (_("unexpected negative value for DW_AT_GNU_numerator"
+ " in DIE at %s"),
+ sect_offset_str (die->sect_off));
+ return;
+ }
+ else if (mpz_sgn (denom.val) == -1)
+ {
+ complaint (_("unexpected negative value for DW_AT_GNU_denominator"
+ " in DIE at %s"),
+ sect_offset_str (die->sect_off));
+ return;
+ }
+
+ *numerator = std::move (num);
+ *denominator = std::move (denom);
+}
+
+/* Assuming that ENCODING is a string whose contents starting at the
+ K'th character is "_nn" where "nn" is a decimal number, scan that
+ number and set RESULT to the value. K is updated to point to the
+ character immediately following the number.
+
+ If the string does not conform to the format described above, false
+ is returned, and K may or may not be changed. */
+
+static bool
+ada_get_gnat_encoded_number (const char *encoding, int &k, gdb_mpz *result)
+{
+ /* The next character should be an underscore ('_') followed
+ by a digit. */
+ if (encoding[k] != '_' || !isdigit (encoding[k + 1]))
+ return false;
+
+ /* Skip the underscore. */
+ k++;
+ int start = k;
+
+ /* Determine the number of digits for our number. */
+ while (isdigit (encoding[k]))
+ k++;
+ if (k == start)
+ return false;
+
+ std::string copy (&encoding[start], k - start);
+ if (mpz_set_str (result->val, copy.c_str (), 10) == -1)
+ return false;
+
+ return true;
+}
+
+/* Scan two numbers from ENCODING at OFFSET, assuming the string is of
+ the form _NN_DD, where NN and DD are decimal numbers. Set NUM and
+ DENOM, update OFFSET, and return true on success. Return false on
+ failure. */
+
+static bool
+ada_get_gnat_encoded_ratio (const char *encoding, int &offset,
+ gdb_mpz *num, gdb_mpz *denom)
+{
+ if (!ada_get_gnat_encoded_number (encoding, offset, num))
+ return false;
+ return ada_get_gnat_encoded_number (encoding, offset, denom);
+}
+
+/* Assuming DIE corresponds to a fixed point type, finish the creation
+ of the corresponding TYPE by setting its type-specific data. CU is
+ the DIE's CU. SUFFIX is the "XF" type name suffix coming from GNAT
+ encodings. It is nullptr if the GNAT encoding should be
+ ignored. */
+
+static void
+finish_fixed_point_type (struct type *type, const char *suffix,
+ struct die_info *die, struct dwarf2_cu *cu)
+{
+ gdb_assert (type->code () == TYPE_CODE_FIXED_POINT
+ && TYPE_SPECIFIC_FIELD (type) == TYPE_SPECIFIC_FIXED_POINT);
+
+ /* If GNAT encodings are preferred, don't examine the
+ attributes. */
+ struct attribute *attr = nullptr;
+ if (suffix == nullptr)
+ {
+ attr = dwarf2_attr (die, DW_AT_binary_scale, cu);
+ if (attr == nullptr)
+ attr = dwarf2_attr (die, DW_AT_decimal_scale, cu);
+ if (attr == nullptr)
+ attr = dwarf2_attr (die, DW_AT_small, cu);
+ }
+
+ /* Numerator and denominator of our fixed-point type's scaling factor.
+ The default is a scaling factor of 1, which we use as a fallback
+ when we are not able to decode it (problem with the debugging info,
+ unsupported forms, bug in GDB, etc...). Using that as the default
+ allows us to at least print the unscaled value, which might still
+ be useful to a user. */
+ gdb_mpz scale_num (1);
+ gdb_mpz scale_denom (1);
+
+ if (attr == nullptr)
+ {
+ int offset = 0;
+ if (suffix != nullptr
+ && ada_get_gnat_encoded_ratio (suffix, offset, &scale_num,
+ &scale_denom)
+ /* The number might be encoded as _nn_dd_nn_dd, where the
+ second ratio is the 'small value. In this situation, we
+ want the second value. */
+ && (suffix[offset] != '_'
+ || ada_get_gnat_encoded_ratio (suffix, offset, &scale_num,
+ &scale_denom)))
+ {
+ /* Found it. */
+ }
+ else
+ {
+ /* Scaling factor not found. Assume a scaling factor of 1,
+ and hope for the best. At least the user will be able to
+ see the encoded value. */
+ scale_num = 1;
+ scale_denom = 1;
+ complaint (_("no scale found for fixed-point type (DIE at %s)"),
+ sect_offset_str (die->sect_off));
+ }
+ }
+ else if (attr->name == DW_AT_binary_scale)
+ {
+ LONGEST scale_exp = attr->constant_value (0);
+ gdb_mpz *num_or_denom = scale_exp > 0 ? &scale_num : &scale_denom;
+
+ mpz_mul_2exp (num_or_denom->val, num_or_denom->val, std::abs (scale_exp));
+ }
+ else if (attr->name == DW_AT_decimal_scale)
+ {
+ LONGEST scale_exp = attr->constant_value (0);
+ gdb_mpz *num_or_denom = scale_exp > 0 ? &scale_num : &scale_denom;
+
+ mpz_ui_pow_ui (num_or_denom->val, 10, std::abs (scale_exp));
+ }
+ else if (attr->name == DW_AT_small)
+ {
+ struct die_info *scale_die;
+ struct dwarf2_cu *scale_cu = cu;
+
+ scale_die = follow_die_ref (die, attr, &scale_cu);
+ if (scale_die->tag == DW_TAG_constant)
+ get_dwarf2_unsigned_rational_constant (scale_die, scale_cu,
+ &scale_num, &scale_denom);
+ else
+ complaint (_("%s DIE not supported as target of DW_AT_small attribute"
+ " (DIE at %s)"),
+ dwarf_tag_name (die->tag), sect_offset_str (die->sect_off));
+ }
+ else
+ {
+ complaint (_("unsupported scale attribute %s for fixed-point type"
+ " (DIE at %s)"),
+ dwarf_attr_name (attr->name),
+ sect_offset_str (die->sect_off));
+ }
+
+ gdb_mpq &scaling_factor = type->fixed_point_info ().scaling_factor;
+ mpz_set (mpq_numref (scaling_factor.val), scale_num.val);
+ mpz_set (mpq_denref (scaling_factor.val), scale_denom.val);
+ mpq_canonicalize (scaling_factor.val);
+}
+
+/* The gnat-encoding suffix for fixed point. */
+
+#define GNAT_FIXED_POINT_SUFFIX "___XF_"
+
+/* If NAME encodes an Ada fixed-point type, return a pointer to the
+ "XF" suffix of the name. The text after this is what encodes the
+ 'small and 'delta information. Otherwise, return nullptr. */
+
+static const char *
+gnat_encoded_fixed_point_type_info (const char *name)
+{
+ return strstr (name, GNAT_FIXED_POINT_SUFFIX);
+}
+