https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126551

            Bug ID: 126551
           Summary: missed vectorization due to bad reduction
                    transformation in miniBUDE
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: missed-optimization
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: arsen at gcc dot gnu.org
                CC: pinskia at gcc dot gnu.org
  Target Milestone: ---
            Target: x86_64-linux-gnu

the following testcase:

  #include <stddef.h>
  #include <stdint.h>
  #include <cmath>
  #include <limits>

  #define ZERO 0.0f
  #define QUARTER 0.25f
  #define HALF 0.5f
  #define ONE 1.0f
  #define TWO 2.0f
  #define FOUR 4.0f
  #define CNSTNT 45.0f

  // Energy evaluation parameters
  #define HBTYPE_F 70
  #define HBTYPE_E 69
  #define HARDNESS 38.0f
  #define NPNPDIST 5.5f
  #define NPPDIST 1.0f

  static constexpr auto FloatMax = std::numeric_limits<float>::max();
  constexpr int PPWI = 1;

  struct __attribute__((__packed__)) Atom {
    float x, y, z;
    int32_t type;
  };

  struct __attribute__((__packed__)) FFParams {
    int32_t hbtype;
    float radius;
    float hphb;
    float elsc;
  };


  void fasten_main(size_t group, size_t ntypes, size_t nposes, size_t natlig,
size_t natpro,        //
                                   const Atom *protein, const Atom *ligand,    
                                    //
                                   const float *transforms_0, const float
*transforms_1, const float *transforms_2, //
                                   const float *transforms_3, const float
*transforms_4, const float *transforms_5, //
                                   const FFParams *forcefield, float *energies 
                                    //
    ) {

      float transform[3][4];
      float etot;

        int ix = group;

        // Compute transformation matrix
        const float sx = std::sin(transforms_0[ix]);
        const float cx = std::cos(transforms_0[ix]);
        const float sy = std::sin(transforms_1[ix]);
        const float cy = std::cos(transforms_1[ix]);
        const float sz = std::sin(transforms_2[ix]);
        const float cz = std::cos(transforms_2[ix]);

        transform[0][0] = cy * cz;
        transform[0][1] = sx * sy * cz - cx * sz;
        transform[0][2] = cx * sy * cz + sx * sz;
        transform[0][3] = transforms_3[ix];
        transform[1][0] = cy * sz;
        transform[1][1] = sx * sy * sz + cx * cz;
        transform[1][2] = cx * sy * sz - sx * cz;
        transform[1][3] = transforms_4[ix];
        transform[2][0] = -sy;
        transform[2][1] = sx * cy;
        transform[2][2] = cx * cy;
        transform[2][3] = transforms_5[ix];

        etot = 0.f;

      // Loop over ligand atoms
      for (int il = 0; il < natlig; il++) {
        // Load ligand atom data
        const Atom l_atom = ligand[il];
        const FFParams l_params = forcefield[l_atom.type];
        const int lhphb_ltz = l_params.hphb < 0.f;
        const int lhphb_gtz = l_params.hphb > 0.f;

        // Transform ligand atom
        float lpos_x, lpos_y, lpos_z;

          lpos_x = transform[0][3] + l_atom.x * transform[0][0] + l_atom.y *
transform[0][1] +
                      l_atom.z * transform[0][2];
          lpos_y = transform[1][3] + l_atom.x * transform[1][0] + l_atom.y *
transform[1][1] +
                      l_atom.z * transform[1][2];
          lpos_z = transform[2][3] + l_atom.x * transform[2][0] + l_atom.y *
transform[2][1] +
                      l_atom.z * transform[2][2];

        // Loop over protein atoms
        for (int ip = 0; ip < natpro; ip++) {
          // Load protein atom data
          const Atom p_atom = protein[ip];
          const FFParams p_params = forcefield[p_atom.type];

          const float radij = p_params.radius + l_params.radius;
          const float r_radij = ONE / radij;

          const float elcdst = (p_params.hbtype == HBTYPE_F && l_params.hbtype
== HBTYPE_F) ? FOUR : TWO;
          const float elcdst1 = (p_params.hbtype == HBTYPE_F && l_params.hbtype
== HBTYPE_F) ? QUARTER : HALF;
          const int type_E = ((p_params.hbtype == HBTYPE_E || l_params.hbtype
== HBTYPE_E));

          const int phphb_ltz = p_params.hphb < 0.f;
          const int phphb_gtz = p_params.hphb > 0.f;
          const int phphb_nz = p_params.hphb != 0.f;
          const float p_hphb = p_params.hphb * (phphb_ltz && lhphb_gtz ? -ONE :
ONE);
          const float l_hphb = l_params.hphb * (phphb_gtz && lhphb_ltz ? -ONE :
ONE);
          const float distdslv = (phphb_ltz ? (lhphb_ltz ? NPNPDIST : NPPDIST)
: (lhphb_ltz ? NPPDIST : -FloatMax));
          const float r_distdslv = ONE / distdslv;

          const float chrg_init = l_params.elsc * p_params.elsc;
          const float dslv_init = p_hphb + l_hphb;

            // Calculate distance between atoms
            const float x = lpos_x - p_atom.x;
            const float y = lpos_y - p_atom.y;
            const float z = lpos_z - p_atom.z;
            const float distij = std::sqrt(x * x + y * y + z * z);

            // Calculate the sum of the sphere radii
            const float distbb = distij - radij;

            const int zone1 = (distbb < ZERO);

            // Calculate steric energy
            etot += (ONE - (distij * r_radij)) * (zone1 ? TWO * HARDNESS :
0.f);

            // Calculate formal and dipole charge interactions
            float chrg_e = chrg_init * ((zone1 ? ONE : (ONE - distbb *
elcdst1)) * (distbb < elcdst ? ONE : ZERO));
            float neg_chrg_e = -std::abs(chrg_e);
            chrg_e = type_E ? neg_chrg_e : chrg_e;
            etot += chrg_e * CNSTNT;

            // Calculate the two cases for Nonpolar-Polar repulsive
interactions
            float coeff = (ONE - (distbb * r_distdslv));
            float dslv_e = dslv_init * ((distbb < distdslv && phphb_nz) ? ONE :
0.f);
            dslv_e *= (zone1 ? ONE : coeff);
            etot += dslv_e;
        }
     }

      // Write result
        energies[group] = etot * HALF;
    }

(extracted from https://github.com/UoB-HPC/miniBUDE OMP w/ PPWI=1)

... fails be vectorized in the inner (natpro) loop on x86-64 w/ -Ofast
-ffast-math -march=x86-64-v4.

applying the following patch:

@@ -123,19 +123,17 @@
           const int zone1 = (distbb < ZERO);

           // Calculate steric energy
-          etot += (ONE - (distij * r_radij)) * (zone1 ? TWO * HARDNESS : 0.f);

           // Calculate formal and dipole charge interactions
           float chrg_e = chrg_init * ((zone1 ? ONE : (ONE - distbb * elcdst1))
* (distbb < elcdst ? ONE : ZERO));
           float neg_chrg_e = -std::abs(chrg_e);
           chrg_e = type_E ? neg_chrg_e : chrg_e;
-          etot += chrg_e * CNSTNT;

           // Calculate the two cases for Nonpolar-Polar repulsive interactions
           float coeff = (ONE - (distbb * r_distdslv));
           float dslv_e = dslv_init * ((distbb < distdslv && phphb_nz) ? ONE :
0.f);
           dslv_e *= (zone1 ? ONE : coeff);
-          etot += dslv_e;
+          etot += ((ONE - (distij * r_radij)) * (zone1 ? TWO * HARDNESS :
0.f)) + (chrg_e * CNSTNT) + dslv_e;
       }
    }

(i.e. merging all of the writes into etot into one write)

... makes it vectorize on x86.

Pinski briefly looked at the testcase, and said:

22:29:49 <pinskia> though it looks like the reduction of etot is not being
handled correctly
22:29:57 <pinskia> etot[0] that is
22:30:44 <pinskia> missed:   reduction used in loop
22:31:38 <pinskia> _359 = etot$_93 + _360;
22:31:38 <pinskia> ...
22:31:38 <pinskia> _355 = _300 ? _359 : etot$_93;
22:33:10 <pinskia>           etot[l] += (ONE - (distij * r_radij)) * (zone1 ?
TWO * HARDNESS : 0.f);
22:34:06 <pinskia> got incorrectly changed into the above rather than temp =
_300 ? _360 : 0.0f; _355 = temp + etot$93;
22:34:16 <pinskia> this is supposed to handled in ifcvt
22:35:48 <pinskia> is_cond_scalar_reduction is supposed to detect that and
create the reduction correctly but it looks like it is not

(note that the testcase declares 'etot' as a variable and not an array, in the
original code it was an array of 1 element)

... this lead me to try merging the +=s

the vectorized version seems to produce a ~15% result on the bm2 deck benchmark
(when compiled with 16.1, I didn't run the benchmark with trunk)

Reply via email to