Edit

kc3-lang/angle/include/CL/cl_half.h

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2021-02-19 17:28:10
    Hash : 6623a70f
    Message : Stubs for OpenCL entry points. Bug: angleproject:5653 Change-Id: I7ec9692a47be2556fef5bdd7630b422cc2d369b9 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2708343 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Cody Northrop <cnorthrop@google.com>

  • include/CL/cl_half.h
  • /*******************************************************************************
     * Copyright (c) 2019-2020 The Khronos Group Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *    http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     ******************************************************************************/
    
    /**
     * This is a header-only utility library that provides OpenCL host code with
     * routines for converting to/from cl_half values.
     *
     * Example usage:
     *
     *    #include <CL/cl_half.h>
     *    ...
     *    cl_half h = cl_half_from_float(0.5f, CL_HALF_RTE);
     *    cl_float f = cl_half_to_float(h);
     */
    
    #ifndef OPENCL_CL_HALF_H
    #define OPENCL_CL_HALF_H
    
    #include <CL/cl_platform.h>
    
    #include <stdint.h>
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    
    /**
     * Rounding mode used when converting to cl_half.
     */
    typedef enum
    {
      CL_HALF_RTE, // round to nearest even
      CL_HALF_RTZ, // round towards zero
      CL_HALF_RTP, // round towards positive infinity
      CL_HALF_RTN, // round towards negative infinity
    } cl_half_rounding_mode;
    
    
    /* Private utility macros. */
    #define CL_HALF_EXP_MASK 0x7C00
    #define CL_HALF_MAX_FINITE_MAG 0x7BFF
    
    
    /*
     * Utility to deal with values that overflow when converting to half precision.
     */
    static inline cl_half cl_half_handle_overflow(cl_half_rounding_mode rounding_mode,
                                                  uint16_t sign)
    {
      if (rounding_mode == CL_HALF_RTZ)
      {
        // Round overflow towards zero -> largest finite number (preserving sign)
        return (sign << 15) | CL_HALF_MAX_FINITE_MAG;
      }
      else if (rounding_mode == CL_HALF_RTP && sign)
      {
        // Round negative overflow towards positive infinity -> most negative finite number
        return (1 << 15) | CL_HALF_MAX_FINITE_MAG;
      }
      else if (rounding_mode == CL_HALF_RTN && !sign)
      {
        // Round positive overflow towards negative infinity -> largest finite number
        return CL_HALF_MAX_FINITE_MAG;
      }
    
      // Overflow to infinity
      return (sign << 15) | CL_HALF_EXP_MASK;
    }
    
    /*
     * Utility to deal with values that underflow when converting to half precision.
     */
    static inline cl_half cl_half_handle_underflow(cl_half_rounding_mode rounding_mode,
                                                   uint16_t sign)
    {
      if (rounding_mode == CL_HALF_RTP && !sign)
      {
        // Round underflow towards positive infinity -> smallest positive value
        return (sign << 15) | 1;
      }
      else if (rounding_mode == CL_HALF_RTN && sign)
      {
        // Round underflow towards negative infinity -> largest negative value
        return (sign << 15) | 1;
      }
    
      // Flush to zero
      return (sign << 15);
    }
    
    
    /**
     * Convert a cl_float to a cl_half.
     */
    static inline cl_half cl_half_from_float(cl_float f, cl_half_rounding_mode rounding_mode)
    {
      // Type-punning to get direct access to underlying bits
      union
      {
        cl_float f;
        uint32_t i;
      } f32;
      f32.f = f;
    
      // Extract sign bit
      uint16_t sign = f32.i >> 31;
    
      // Extract FP32 exponent and mantissa
      uint32_t f_exp = (f32.i >> (CL_FLT_MANT_DIG - 1)) & 0xFF;
      uint32_t f_mant = f32.i & ((1 << (CL_FLT_MANT_DIG - 1)) - 1);
    
      // Remove FP32 exponent bias
      int32_t exp = f_exp - CL_FLT_MAX_EXP + 1;
    
      // Add FP16 exponent bias
      uint16_t h_exp = (uint16_t)(exp + CL_HALF_MAX_EXP - 1);
    
      // Position of the bit that will become the FP16 mantissa LSB
      uint32_t lsb_pos = CL_FLT_MANT_DIG - CL_HALF_MANT_DIG;
    
      // Check for NaN / infinity
      if (f_exp == 0xFF)
      {
        if (f_mant)
        {
          // NaN -> propagate mantissa and silence it
          uint16_t h_mant = (uint16_t)(f_mant >> lsb_pos);
          h_mant |= 0x200;
          return (sign << 15) | CL_HALF_EXP_MASK | h_mant;
        }
        else
        {
          // Infinity -> zero mantissa
          return (sign << 15) | CL_HALF_EXP_MASK;
        }
      }
    
      // Check for zero
      if (!f_exp && !f_mant)
      {
        return (sign << 15);
      }
    
      // Check for overflow
      if (exp >= CL_HALF_MAX_EXP)
      {
        return cl_half_handle_overflow(rounding_mode, sign);
      }
    
      // Check for underflow
      if (exp < (CL_HALF_MIN_EXP - CL_HALF_MANT_DIG - 1))
      {
        return cl_half_handle_underflow(rounding_mode, sign);
      }
    
      // Check for value that will become denormal
      if (exp < -14)
      {
        // Denormal -> include the implicit 1 from the FP32 mantissa
        h_exp = 0;
        f_mant |= 1 << (CL_FLT_MANT_DIG - 1);
    
        // Mantissa shift amount depends on exponent
        lsb_pos = -exp + (CL_FLT_MANT_DIG - 25);
      }
    
      // Generate FP16 mantissa by shifting FP32 mantissa
      uint16_t h_mant = (uint16_t)(f_mant >> lsb_pos);
    
      // Check whether we need to round
      uint32_t halfway = 1 << (lsb_pos - 1);
      uint32_t mask = (halfway << 1) - 1;
      switch (rounding_mode)
      {
        case CL_HALF_RTE:
          if ((f_mant & mask) > halfway)
          {
            // More than halfway -> round up
            h_mant += 1;
          }
          else if ((f_mant & mask) == halfway)
          {
            // Exactly halfway -> round to nearest even
            if (h_mant & 0x1)
              h_mant += 1;
          }
          break;
        case CL_HALF_RTZ:
          // Mantissa has already been truncated -> do nothing
          break;
        case CL_HALF_RTP:
          if ((f_mant & mask) && !sign)
          {
            // Round positive numbers up
            h_mant += 1;
          }
          break;
        case CL_HALF_RTN:
          if ((f_mant & mask) && sign)
          {
            // Round negative numbers down
            h_mant += 1;
          }
          break;
      }
    
      // Check for mantissa overflow
      if (h_mant & 0x400)
      {
        h_exp += 1;
        h_mant = 0;
      }
    
      return (sign << 15) | (h_exp << 10) | h_mant;
    }
    
    
    /**
     * Convert a cl_double to a cl_half.
     */
    static inline cl_half cl_half_from_double(cl_double d, cl_half_rounding_mode rounding_mode)
    {
      // Type-punning to get direct access to underlying bits
      union
      {
        cl_double d;
        uint64_t i;
      } f64;
      f64.d = d;
    
      // Extract sign bit
      uint16_t sign = f64.i >> 63;
    
      // Extract FP64 exponent and mantissa
      uint64_t d_exp = (f64.i >> (CL_DBL_MANT_DIG - 1)) & 0x7FF;
      uint64_t d_mant = f64.i & (((uint64_t)1 << (CL_DBL_MANT_DIG - 1)) - 1);
    
      // Remove FP64 exponent bias
      int64_t exp = d_exp - CL_DBL_MAX_EXP + 1;
    
      // Add FP16 exponent bias
      uint16_t h_exp = (uint16_t)(exp + CL_HALF_MAX_EXP - 1);
    
      // Position of the bit that will become the FP16 mantissa LSB
      uint32_t lsb_pos = CL_DBL_MANT_DIG - CL_HALF_MANT_DIG;
    
      // Check for NaN / infinity
      if (d_exp == 0x7FF)
      {
        if (d_mant)
        {
          // NaN -> propagate mantissa and silence it
          uint16_t h_mant = (uint16_t)(d_mant >> lsb_pos);
          h_mant |= 0x200;
          return (sign << 15) | CL_HALF_EXP_MASK | h_mant;
        }
        else
        {
          // Infinity -> zero mantissa
          return (sign << 15) | CL_HALF_EXP_MASK;
        }
      }
    
      // Check for zero
      if (!d_exp && !d_mant)
      {
        return (sign << 15);
      }
    
      // Check for overflow
      if (exp >= CL_HALF_MAX_EXP)
      {
        return cl_half_handle_overflow(rounding_mode, sign);
      }
    
      // Check for underflow
      if (exp < (CL_HALF_MIN_EXP - CL_HALF_MANT_DIG - 1))
      {
        return cl_half_handle_underflow(rounding_mode, sign);
      }
    
      // Check for value that will become denormal
      if (exp < -14)
      {
        // Include the implicit 1 from the FP64 mantissa
        h_exp = 0;
        d_mant |= (uint64_t)1 << (CL_DBL_MANT_DIG - 1);
    
        // Mantissa shift amount depends on exponent
        lsb_pos = (uint32_t)(-exp + (CL_DBL_MANT_DIG - 25));
      }
    
      // Generate FP16 mantissa by shifting FP64 mantissa
      uint16_t h_mant = (uint16_t)(d_mant >> lsb_pos);
    
      // Check whether we need to round
      uint64_t halfway = (uint64_t)1 << (lsb_pos - 1);
      uint64_t mask = (halfway << 1) - 1;
      switch (rounding_mode)
      {
        case CL_HALF_RTE:
          if ((d_mant & mask) > halfway)
          {
            // More than halfway -> round up
            h_mant += 1;
          }
          else if ((d_mant & mask) == halfway)
          {
            // Exactly halfway -> round to nearest even
            if (h_mant & 0x1)
              h_mant += 1;
          }
          break;
        case CL_HALF_RTZ:
          // Mantissa has already been truncated -> do nothing
          break;
        case CL_HALF_RTP:
          if ((d_mant & mask) && !sign)
          {
            // Round positive numbers up
            h_mant += 1;
          }
          break;
        case CL_HALF_RTN:
          if ((d_mant & mask) && sign)
          {
            // Round negative numbers down
            h_mant += 1;
          }
          break;
      }
    
      // Check for mantissa overflow
      if (h_mant & 0x400)
      {
        h_exp += 1;
        h_mant = 0;
      }
    
      return (sign << 15) | (h_exp << 10) | h_mant;
    }
    
    
    /**
     * Convert a cl_half to a cl_float.
     */
    static inline cl_float cl_half_to_float(cl_half h)
    {
      // Type-punning to get direct access to underlying bits
      union
      {
        cl_float f;
        uint32_t i;
      } f32;
    
      // Extract sign bit
      uint16_t sign = h >> 15;
    
      // Extract FP16 exponent and mantissa
      uint16_t h_exp = (h >> (CL_HALF_MANT_DIG - 1)) & 0x1F;
      uint16_t h_mant = h & 0x3FF;
    
      // Remove FP16 exponent bias
      int32_t exp = h_exp - CL_HALF_MAX_EXP + 1;
    
      // Add FP32 exponent bias
      uint32_t f_exp = exp + CL_FLT_MAX_EXP - 1;
    
      // Check for NaN / infinity
      if (h_exp == 0x1F)
      {
        if (h_mant)
        {
          // NaN -> propagate mantissa and silence it
          uint32_t f_mant = h_mant << (CL_FLT_MANT_DIG - CL_HALF_MANT_DIG);
          f_mant |= 0x400000;
          f32.i = (sign << 31) | 0x7F800000 | f_mant;
          return f32.f;
        }
        else
        {
          // Infinity -> zero mantissa
          f32.i = (sign << 31) | 0x7F800000;
          return f32.f;
        }
      }
    
      // Check for zero / denormal
      if (h_exp == 0)
      {
        if (h_mant == 0)
        {
          // Zero -> zero exponent
          f_exp = 0;
        }
        else
        {
          // Denormal -> normalize it
          // - Shift mantissa to make most-significant 1 implicit
          // - Adjust exponent accordingly
          uint32_t shift = 0;
          while ((h_mant & 0x400) == 0)
          {
            h_mant <<= 1;
            shift++;
          }
          h_mant &= 0x3FF;
          f_exp -= shift - 1;
        }
      }
    
      f32.i = (sign << 31) | (f_exp << 23) | (h_mant << 13);
      return f32.f;
    }
    
    
    #undef CL_HALF_EXP_MASK
    #undef CL_HALF_MAX_FINITE_MAG
    
    
    #ifdef __cplusplus
    }
    #endif
    
    
    #endif  /* OPENCL_CL_HALF_H */