cxx-ada-int128-types

C++ Ada unsigned __int128 types

GCC 13 and 14 emit the internal C++ name for unsigned __int128 as an undefined Ada identifier.

AcceptedApplies standaloneSince 1.2.0

Where it applies.

How each patchset treats this bundle on each GCC major
PatchsetGCC 13GCC 14GCC 15GCC 16
1.2.0 (latest)Patchedgcc-13-1413.2.0Patchedgcc-13-1414.2.0Known-good control15.3.0Known-good control16.2.0
1.1.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.2.0
1.0.1Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0
1.0.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0

Explanation.

GCC 13 and 14 special-case the internal C++ type name __int128, but compare the complete spelling. That recognizes the signed type and misses the internal unsigned spelling, __int128 unsigned.

The offending C++ is:

using Signed_128 = __int128;
using Unsigned_128 = unsigned __int128;

Signed_128 signed_round_trip (Signed_128);
Unsigned_128 unsigned_round_trip (Unsigned_128);

The GCC 13/14 mapper currently produces one valid subtype and one reference to an Ada identifier that does not exist:

subtype Signed_128 is Extensions.Signed_128;
subtype Unsigned_128 is uu_int128_unsigned;

It should recognize both internal names as 128-bit integer types and produce:

subtype Signed_128 is Extensions.Signed_128;
subtype Unsigned_128 is Extensions.Unsigned_128;

The correction uses the eight-character __int128 prefix comparison already present in GCC 15 and 16. Those releases are known-good controls and receive no code patch. The executable regression passes negative signed and high-bit unsigned values by value from C++ to Ada, back through C++, and into C++ value checkers at -O0 and -O2.

Run it against an unpatched or patched compiler root:

./bundles/cxx-ada-int128-types/run-test.sh \
  TOOLCHAIN_ROOT GCC_VERSION unpatched
./bundles/cxx-ada-int128-types/run-test.sh \
  TOOLCHAIN_ROOT GCC_VERSION patched

Patch.

Variant gcc-13-14

Applies to 13.2.0, 14.2.0. Source flavors: linux, darwin_arm64.

+24 −2 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +2−2modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -2193 +2193 @@dump_ada_node (pretty_printer *buffer, tree node, tree type, int spc,
2193Removed line. && !strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node))),
2194Removed line. "__int128")))
2193Added line. && !strncmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (node))),
2194Added line. "__int128", 8)))
gcc/testsuite/g++.dg/ada-spec/int128-types.C +22−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/int128-types.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-options "-fdump-ada-spec-slim" } */
3Added line. /* { dg-final { scan-file int128_types_c.ads "subtype Signed_128 is Extensions.Signed_128" } } */
4Added line. /* { dg-final { scan-file int128_types_c.ads "subtype Unsigned_128 is Extensions.Unsigned_128" } } */
5Added line.
6Added line. using Signed_128 = __int128;
7Added line. using Unsigned_128 = unsigned __int128;
8Added line.
9Added line. static const Signed_128 signed_expected
10Added line. = -((static_cast<Signed_128> (1) << 100) + 12345);
11Added line. static const Unsigned_128 unsigned_expected
12Added line. = (static_cast<Unsigned_128> (1) << 127)
13Added line. | (static_cast<Unsigned_128> (1) << 80) | 67890;
14Added line.
15Added line. Signed_128 signed_seed () { return signed_expected; }
16Added line. Unsigned_128 unsigned_seed () { return unsigned_expected; }
17Added line.
18Added line. Signed_128 signed_round_trip (Signed_128 value) { return value; }
19Added line. Unsigned_128 unsigned_round_trip (Unsigned_128 value) { return value; }
20Added line.
21Added line. int signed_matches (Signed_128 value) { return value == signed_expected; }
22Added line. int unsigned_matches (Unsigned_128 value) { return value == unsigned_expected; }
23

Tests.

int128-types.C C++ · 22 lines
/* { dg-do compile } */
/* { dg-options "-fdump-ada-spec-slim" } */
/* { dg-final { scan-file int128_types_c.ads "subtype Signed_128 is Extensions.Signed_128" } } */
/* { dg-final { scan-file int128_types_c.ads "subtype Unsigned_128 is Extensions.Unsigned_128" } } */

using Signed_128 = __int128;
using Unsigned_128 = unsigned __int128;

static const Signed_128 signed_expected
  = -((static_cast<Signed_128> (1) << 100) + 12345);
static const Unsigned_128 unsigned_expected
  = (static_cast<Unsigned_128> (1) << 127)
    | (static_cast<Unsigned_128> (1) << 80) | 67890;

Signed_128 signed_seed () { return signed_expected; }
Unsigned_128 unsigned_seed () { return unsigned_expected; }

Signed_128 signed_round_trip (Signed_128 value) { return value; }
Unsigned_128 unsigned_round_trip (Unsigned_128 value) { return value; }

int signed_matches (Signed_128 value) { return value == signed_expected; }
int unsigned_matches (Unsigned_128 value) { return value == unsigned_expected; }

Download · View in repository

int128_types_consumer.adb Ada · 15 lines
with Interfaces.C;
with Int128_Types_C;

procedure Int128_Types_Consumer is
   use type Interfaces.C.int;
   use Int128_Types_C;
begin
   if signed_matches (signed_round_trip (signed_seed)) /= 1 then
      raise Program_Error with "signed __int128 round trip failed";
   end if;

   if unsigned_matches (unsigned_round_trip (unsigned_seed)) /= 1 then
      raise Program_Error with "unsigned __int128 round trip failed";
   end if;
end Int128_Types_Consumer;

Download · View in repository

run-test.sh shell · 78 lines
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -ne 3 ]]; then
  echo "usage: $0 TOOLCHAIN_ROOT GCC_VERSION unpatched|patched" >&2
  exit 2
fi

root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
source "$root/scripts/regression-common.sh"
resolve_regression_toolchain "$1"
version=$2
major=${version%%.*}
state=$3
[[ "$state" == unpatched || "$state" == patched ]] || {
  echo "error: state must be unpatched or patched" >&2
  exit 2
}

gxx="$REGRESSION_TOOLCHAIN/bin/g++"
[[ -x "$gxx" ]] || {
  echo "error: no g++ in $REGRESSION_TOOLCHAIN" >&2
  exit 1
}

cxx_fixture="$root/bundles/cxx-ada-int128-types/tests/int128-types.C"
ada_fixture="$root/bundles/cxx-ada-int128-types/tests/int128_types_consumer.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-int128-types.XXXXXX")
trap 'rm -rf "$work"' EXIT

for optimization in 0 2; do
  case_dir="$work/O$optimization"
  mkdir -p "$case_dir"
  cp "$cxx_fixture" "$ada_fixture" "$case_dir/"
  (
    cd "$case_dir"
    "${REGRESSION_ENV[@]}" "$gxx" -c "-O$optimization" \
      -fdump-ada-spec-slim int128-types.C
  )

  spec="$case_dir/int128_types_c.ads"
  [[ -f "$spec" ]] || {
    echo "error: g++ did not generate $spec" >&2
    exit 1
  }

  set +e
  (
    cd "$case_dir"
    "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f \
      "-O$optimization" int128_types_consumer.adb \
      -largs int128-types.o -lstdc++
    "${REGRESSION_ENV[@]}" ./int128_types_consumer
  ) >"$case_dir/run.log" 2>&1
  run_status=$?
  set -e

  if [[ "$state" == unpatched && ( "$major" == 13 || "$major" == 14 ) ]]; then
    [[ $run_status -ne 0 ]] || {
      echo "error: unpatched __int128 regression unexpectedly passed" >&2
      exit 1
    }
    grep -Eiq 'uu_int128_unsigned.*undefined' "$case_dir/run.log" || {
      cat "$case_dir/run.log"
      exit 1
    }
    echo "cxx-ada-int128-types -O$optimization: expected undefined unsigned type (GCC $version)"
    continue
  fi

  [[ $run_status -eq 0 ]] || {
    cat "$case_dir/run.log"
    exit 1
  }
  grep -Eq 'Extensions\.Signed_128' "$spec"
  grep -Eq 'Extensions\.Unsigned_128' "$spec"
  echo "cxx-ada-int128-types -O$optimization: supported (GCC $version, $state)"
done

Download · View in repository

Commands.

Apply the patch
patch --fuzz=0 -p1 -i bundles/cxx-ada-int128-types/patches/gcc-13-14.patch
Build the compiler
PATH=/path/to/bootstrap/bin:$PATH ./scripts/build-gnat.sh SOURCE BUILD INSTALL
Run the regression
./scripts/run-regressions.sh INSTALL 1.2.0 GCC_MAJOR patched

Metadata.