Skip to content

Miscomparison of signed-zeros #167376

@tuliom

Description

@tuliom

A recent PR to llvm-test-suite enabled test fmax-reduction.
This test was getting skipped before this PR got merged.

After that, we started seeing this test to fail on all architectures supported by Fedora with the following error:

FAIL: test-suite :: SingleSource/UnitTests/Vectorizer/fmax-reduction.test (2384 of 2423)
******************** TEST 'test-suite :: SingleSource/UnitTests/Vectorizer/fmax-reduction.test' FAILED ********************

/var/tmp/llvm-test-suite.kwn8stIzGS/tools/timeit-target --timeout 7200 --limit-core 0 --limit-cpu 7200 --limit-file-size 209715200 --limit-rss-size 838860800 --append-exitstatus --redirect-output /var/tmp/llvm-test-suite.kwn8stIzGS/SingleSource/UnitTests/Vectorizer/Output/fmax-reduction.test.out --redirect-input /dev/null --chdir /var/tmp/llvm-test-suite.kwn8stIzGS/SingleSource/UnitTests/Vectorizer --summary /var/tmp/llvm-test-suite.kwn8stIzGS/SingleSource/UnitTests/Vectorizer/Output/fmax-reduction.test.time /var/tmp/llvm-test-suite.kwn8stIzGS/SingleSource/UnitTests/Vectorizer/fmax-reduction
/var/tmp/llvm-test-suite.kwn8stIzGS/tools/fpcmp-target /var/tmp/llvm-test-suite.kwn8stIzGS/SingleSource/UnitTests/Vectorizer/Output/fmax-reduction.test.out /var/tmp/llvm-test-suite.kwn8stIzGS/SingleSource/UnitTests/Vectorizer/fmax-reduction.reference_output

+ /var/tmp/llvm-test-suite.kwn8stIzGS/tools/fpcmp-target /var/tmp/llvm-test-suite.kwn8stIzGS/SingleSource/UnitTests/Vectorizer/Output/fmax-reduction.test.out /var/tmp/llvm-test-suite.kwn8stIzGS/SingleSource/UnitTests/Vectorizer/fmax-reduction.reference_output
/var/tmp/llvm-test-suite.kwn8stIzGS/tools/fpcmp-target: Comparison failed, textual difference between 'M' and 'C'

Input 1:
Checking fmaxnum_start_neg_2
Miscompare signed-zeros: -0 != 0
exit 1

Input 2:
Checking fmaxnum_start_neg_2
Checking fmaxnum_start_min
Checking fmaxnum_start_denorm_min
Checking fmaxnum_start_is_nan
Checking fmax_strict_start_neg_2
Checking fmax_strict_start_min
Checking fmax_strict_start_denorm_min
Checking fmax_strict_start_nan
Checking fmax_non_strict_start_neg_2
Checking fmax_cmp_max_gt_start_neg_2
Checking fmax_cmp_max_lt_start_neg_2
Checking fmax_cmp_max_lt_start_denorm_min
Checking fmax_cmp_max_lt_start_neg_nan
exit 0

I reduced the test to:

#include <algorithm>
#include <functional>
#include <iostream>
#include <limits>
#include <memory>
#include <stdint.h>

#include "common.h"

static bool isEqual(float A, float B) {
  if (std::isnan(A) || std::isnan(B))
    return std::isnan(A) && std::isnan(B);

  if (A == 0.0f)
    return B == 0.0f && std::signbit(A) == std::signbit(B);

  return A == B;
}

template <typename Ty> using Fn1Ty = std::function<Ty(Ty *, unsigned)>;

template <typename Ty>
static void check(Fn1Ty<Ty> ScalarFn, Fn1Ty<Ty> VectorFn, float *Src,
                  unsigned N, const char *Type) {
  auto Reference = ScalarFn(Src, N);
  auto ToCheck = VectorFn(Src, N);
  if (!isEqual(Reference, ToCheck)) {
    std::cerr << "Miscompare " << Type << ": " << Reference << " != " << ToCheck
              << "\n";
    exit(1);
  }
}

template <typename Ty>
static void checkVectorFunction(Fn1Ty<Ty> ScalarFn, Fn1Ty<Ty> VectorFn,
                                const char *Name) {
  std::cout << "Checking " << Name << "\n";

  unsigned N = 1024;
  std::unique_ptr<Ty[]> Src1(new Ty[N]);
  init_data(Src1, N);

  // Check with multiple signed-zeros at different positions.
  for (unsigned Idx = 0; Idx != 64; ++Idx) {
    for (unsigned I = 0; I != N; ++I)
      Src1[I] = -1.0;

    for (unsigned Offset = 1; Offset != 32; ++Offset) {
      Src1[Idx] = -0.0;
      Src1[Idx + Offset] = +0.0;

      check(ScalarFn, VectorFn, &Src1[0], N, "signed-zeros");
    }
  }

  for (unsigned Idx = 0; Idx != 64; ++Idx) {
    for (unsigned I = 0; I != N; ++I)
      Src1[I] = -1.0;

    for (unsigned Offset = 1; Offset != 32; ++Offset) {
      Src1[Idx] = +0.0;
      Src1[Idx + Offset] = -0.0;

      check(ScalarFn, VectorFn, &Src1[0], N, "signed-zeros");
    }
  }
}

int main(void) {
  {
    DEFINE_SCALAR_AND_VECTOR_FN1_TYPE(
        float Max = -2.0;, for (unsigned I = 0; I < 1024;
                                I++) { Max = std::fmax(Max, A[I]); } return Max;
        , float);
    checkVectorFunction<float>(ScalarFn, VectorFn, "fmaxnum_start_neg_2");
  }

  return 0;
}

I can reproduce it with the following command:

$ clang++  -O1 ./fmax-reduction.cpp -o fmax-reduction -lstdc++ -lm && ./fmax-reduction
Checking fmaxnum_start_neg_2
Miscompare signed-zeros: -0 != 0

I bisected this issue to commit 004c67e (PR #148239).
Heads up: this commit has been backported to LLVM 21.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions