/******************************************************************************* * * MIT License * * Copyright (C) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * *******************************************************************************/ #pragma once #include "ProgramOptions.hpp" #include #include "ClientProblemFactory.hpp" #include #include #include "RunListener.hpp" namespace po = Tensile::Client::po; namespace Tensile { namespace Client { // Problem-indept. from 0~7, and 16 (fixed values for every problem) // And problem-dept. from 8~15 (values depend on problem) enum class InitMode { Zero = 0, // 0 One, // 1 Two, // 2 Random, // 3 NaN, // 4 Inf, // 5 BadInput, // 6 BadOutput, // 7 SerialIdx, // 8 SerialDim0, // 9 SerialDim1, // 10 Identity, // 11 TrigSin, // 12 TrigCos, // 13 TrigAbsSin, // 14 TrigAbsCos, // 15 RandomNarrow, // 16 NegOne, // 17 Max, // 18 DenormMin, // 19 DenormMax, // 20 Count }; static bool IsProblemDependent(InitMode const& mode) { return mode == InitMode::SerialIdx || mode == InitMode::SerialDim0 || mode == InitMode::SerialDim1 || mode == InitMode::Identity || mode == InitMode::TrigSin || mode == InitMode::TrigCos || mode == InitMode::TrigAbsSin || mode == InitMode::TrigAbsCos; } std::string ToString(InitMode mode); std::ostream& operator<<(std::ostream& stream, InitMode const& mode); std::istream& operator>>(std::istream& stream, InitMode& mode); const int pageSize = 2 * 1024 * 1024; enum class BoundsCheckMode { Disable = 0, NaN, GuardPageFront, GuardPageBack, GuardPageAll, MaxMode }; std::ostream& operator<<(std::ostream& stream, BoundsCheckMode const& mode); std::istream& operator>>(std::istream& stream, BoundsCheckMode& mode); template class TypedDataInitialization; class DataInitialization : public RunListener { public: static double GetRepresentativeBetaValue(po::variables_map const& args); /** * Factory function. */ static std::shared_ptr Get(po::variables_map const& args, ClientProblemFactory const& problemFactory, size_t maxWorkspaceSize = 0); template static std::shared_ptr> GetTyped(po::variables_map const& args, ClientProblemFactory const& problemFactory, size_t maxWorkspaceSize = 0); DataInitialization(po::variables_map const& args, ClientProblemFactory const& problemFactory, size_t maxWorkspaceSize = 0); ~DataInitialization(); /** * Returns a ContractionInputs object with pointers to CPU memory, * suitable for using to calculate reference results. */ virtual std::shared_ptr prepareCPUInputs(ContractionProblem const& problem) = 0; /** * Returns a ContractionInputs object with pointers to GPU memory, * suitable for using to run the kernel. */ virtual std::shared_ptr prepareGPUInputs(ContractionProblem const& problem) = 0; virtual std::shared_ptr cpuConvInputs() const = 0; template static T getValue(InitMode mode) { switch(mode) { case InitMode::Zero: return getValue(); case InitMode::One: return getValue(); case InitMode::Two: return getValue(); case InitMode::Random: return getValue(); case InitMode::RandomNarrow: return getValue(); case InitMode::NaN: return getValue(); case InitMode::Inf: return getValue(); case InitMode::BadInput: return getValue(); case InitMode::BadOutput: return getValue(); case InitMode::NegOne: return getValue(); case InitMode::Max: return getValue(); case InitMode::DenormMin: return getValue(); case InitMode::DenormMax: return getValue(); case InitMode::SerialIdx: case InitMode::SerialDim0: case InitMode::SerialDim1: case InitMode::Identity: case InitMode::TrigSin: case InitMode::TrigCos: case InitMode::TrigAbsSin: case InitMode::TrigAbsCos: case InitMode::Count: throw std::runtime_error("Invalid InitMode."); } } template static inline T getValue(); template static inline T getTrigValue(int idx, bool useCos, bool useAbs); template static bool isBadInput(T value); template static bool isBadOutput(T value); // Fills max buffer size template void initArray(InitMode mode, T* array, size_t elements) { switch(mode) { case InitMode::Zero: initArray(array, elements); break; case InitMode::One: initArray(array, elements); break; case InitMode::Two: initArray(array, elements); break; case InitMode::Random: initArray(array, elements); break; case InitMode::RandomNarrow: initArray(array, elements); break; case InitMode::NaN: initArray(array, elements); break; case InitMode::Inf: initArray(array, elements); break; case InitMode::BadInput: initArray(array, elements); break; case InitMode::BadOutput: initArray(array, elements); break; case InitMode::NegOne: initArray(array, elements); break; case InitMode::Max: initArray(array, elements); break; case InitMode::DenormMin: initArray(array, elements); break; case InitMode::DenormMax: initArray(array, elements); break; case InitMode::SerialIdx: case InitMode::SerialDim0: case InitMode::SerialDim1: case InitMode::Identity: case InitMode::TrigSin: case InitMode::TrigCos: case InitMode::TrigAbsSin: case InitMode::TrigAbsCos: case InitMode::Count: throw std::runtime_error("Invalid InitMode."); } } // For problem dependent data initialization template void initArray(InitMode mode, T* array, TensorDescriptor const& tensor) { switch(mode) { case InitMode::Zero: initArray(array, tensor); break; case InitMode::One: initArray(array, tensor); break; case InitMode::Two: initArray(array, tensor); break; case InitMode::Random: initArray(array, tensor); break; case InitMode::RandomNarrow: initArray(array, tensor); break; case InitMode::NaN: initArray(array, tensor); break; case InitMode::Inf: initArray(array, tensor); break; case InitMode::BadInput: initArray(array, tensor); break; case InitMode::BadOutput: initArray(array, tensor); break; case InitMode::NegOne: initArray(array, tensor); break; case InitMode::Max: initArray(array, tensor); break; case InitMode::DenormMin: initArray(array, tensor); break; case InitMode::DenormMax: initArray(array, tensor); break; case InitMode::SerialIdx: initArraySerialIdx(array, tensor); break; case InitMode::SerialDim0: initArraySerialDim(array, 0, tensor); break; case InitMode::SerialDim1: initArraySerialDim(array, 1, tensor); break; case InitMode::Identity: initArrayIdentity(array, tensor); break; case InitMode::TrigSin: initArrayTrig(array, tensor); break; case InitMode::TrigCos: initArrayTrig(array, tensor); break; case InitMode::TrigAbsSin: initArrayTrig(array, tensor); break; case InitMode::TrigAbsCos: initArrayTrig(array, tensor); break; case InitMode::Count: throw std::runtime_error("Invalid InitMode."); } } template void initArray(T* array, size_t elements) { for(size_t i = 0; i < elements; i++) { array[i] = getValue(); } } template void initArray(T* array, TensorDescriptor const& tensor) { size_t elements = tensor.totalAllocatedElements(); initArray(array, elements); } template void initArraySerialIdx(T* array, TensorDescriptor const& tensor) { auto const& sizes = tensor.sizes(); auto count = CoordCount(sizes.begin(), sizes.end()); std::vector coord(tensor.dimensions(), 0); for(size_t idx = 0; idx < count; idx++) { CoordNumbered(idx, coord.begin(), coord.end(), sizes.begin(), sizes.end()); array[tensor.index(coord)] = static_cast(idx); } } template void initArraySerialDim(T* array, int dim, TensorDescriptor const& tensor) { auto const& sizes = tensor.sizes(); auto count = CoordCount(sizes.begin(), sizes.end()); std::vector coord(tensor.dimensions(), 0); for(size_t idx = 0; idx < count; idx++) { CoordNumbered(idx, coord.begin(), coord.end(), sizes.begin(), sizes.end()); array[tensor.index(coord)] = static_cast(coord[dim]); } } template void initArrayIdentity(T* array, TensorDescriptor const& tensor) { auto const& sizes = tensor.sizes(); auto count = CoordCount(sizes.begin(), sizes.end()); std::vector coord(tensor.dimensions(), 0); for(size_t idx = 0; idx < count; idx++) { CoordNumbered(idx, coord.begin(), coord.end(), sizes.begin(), sizes.end()); array[tensor.index(coord)] = static_cast(coord[0] == coord[1] ? 1 : 0); } } template void initArrayTrig(T* array, TensorDescriptor const& tensor) { auto const& sizes = tensor.sizes(); auto count = CoordCount(sizes.begin(), sizes.end()); std::vector coord(tensor.dimensions(), 0); for(size_t idx = 0; idx < count; idx++) { CoordNumbered(idx, coord.begin(), coord.end(), sizes.begin(), sizes.end()); array[tensor.index(coord)] = getTrigValue(idx, useCos, useAbs); } } size_t workspaceSize() const { return m_workspaceSize; } BoundsCheckMode getCurBoundsCheck() { return m_curBoundsCheck; } virtual bool needMoreBenchmarkRuns() const override { return false; }; virtual void preBenchmarkRun() override{}; virtual void postBenchmarkRun() override{}; virtual void preProblem(ContractionProblem const& problem) override{}; virtual void postProblem() override{}; virtual void preSolution(ContractionSolution const& solution) override{}; virtual void postSolution() override{}; virtual bool needMoreRunsInSolution() const override { return m_numRunsInSolution < m_numRunsPerSolution; }; virtual size_t numWarmupRuns() override { return 0; }; virtual void setNumWarmupRuns(size_t count) override{}; virtual void preWarmup() override{}; virtual void postWarmup() override{}; virtual void validateWarmups(std::shared_ptr inputs, TimingEvents const& startEvents, TimingEvents const& stopEvents) override { m_numRunsInSolution++; }; virtual size_t numSyncs() override { return 0; }; virtual void setNumSyncs(size_t count) override{}; virtual void preSyncs() override{}; virtual void postSyncs() override{}; virtual size_t numEnqueuesPerSync() override { return 0; }; virtual void setNumEnqueuesPerSync(size_t count) override{}; virtual void preEnqueues() override{}; virtual void postEnqueues(TimingEvents const& startEvents, TimingEvents const& stopEvents) override{}; virtual void validateEnqueues(std::shared_ptr inputs, TimingEvents const& startEvents, TimingEvents const& stopEvents) override{}; virtual void finalizeReport() override{}; virtual int error() const override { return 0; }; protected: InitMode m_aInit, m_bInit, m_cInit, m_dInit; InitMode m_alphaInit, m_betaInit; size_t m_aMaxElements; size_t m_bMaxElements; size_t m_cMaxElements; size_t m_dMaxElements; size_t m_maxBatch; size_t m_workspaceSize; bool m_stridedBatched; bool m_cEqualsD; bool m_convolutionVsContraction; int m_elementsToValidate = 0; /// If true, we will allocate an extra copy of the inputs on the GPU. /// This will improve performance as we don't have to copy from the CPU /// with each kernel launch, but it will use extra memory. bool m_keepPristineCopyOnGPU = true; /// If set "::NaN", we will initialize all out-of-bounds inputs to NaN, and /// all out-of-bounds outputs to a known value. This allows us to /// verify that out-of-bounds values are not used or written to. /// If set "::GuardPageFront/::GuardPageBack", we will allocate matrix memory /// with page aligned, and put matrix start/end address to memory start/end address. /// Out-of-bounds access would trigger memory segmentation faults. /// m_boundsCheck keep the setting from args. /// m_curBoundsCheck keep the current running boundsCheck mode. /// If set "::GuardPageAll", DataInit would need 2 runs per solution. /// First run would apply "::GuardPageFront" and second run would apply "::GuardPageBack". BoundsCheckMode m_boundsCheck = BoundsCheckMode::Disable; BoundsCheckMode m_curBoundsCheck = BoundsCheckMode::Disable; int m_numRunsPerSolution = 0; int m_numRunsInSolution = 0; /// If true, the data is dependent on the problem size (e.g. serial) /// and must be reinitialized for each problem. Pristine copy on GPU /// cannot be used with problem dependent data. bool m_problemDependentData = false; }; template <> inline float DataInitialization::getValue() { return 0.0f; } template <> inline float DataInitialization::getValue() { return 1.0f; } template <> inline float DataInitialization::getValue() { return 2.0f; } template <> inline float DataInitialization::getValue() { return -1.0f; } template <> inline float DataInitialization::getValue() { return std::numeric_limits::max(); } template <> inline float DataInitialization::getValue() { return std::numeric_limits::denorm_min(); } template <> inline float DataInitialization::getValue() { union { uint32_t bits; float value; } x; x.bits = 0x007FFFFF; return x.value; } template <> inline float DataInitialization::getValue() { return std::numeric_limits::quiet_NaN(); } template <> inline float DataInitialization::getValue() { return std::numeric_limits::infinity(); } template <> inline float DataInitialization::getValue() { return static_cast((rand() % 201) - 100); } template <> inline float DataInitialization::getValue() { return getValue(); } template <> inline float DataInitialization::getValue() { return getValue(); } template <> inline double DataInitialization::getValue() { return 0.0; } template <> inline double DataInitialization::getValue() { return 1.0; } template <> inline double DataInitialization::getValue() { return 2.0; } template <> inline double DataInitialization::getValue() { return -1.0; } template <> inline double DataInitialization::getValue() { return std::numeric_limits::max(); } template <> inline double DataInitialization::getValue() { return std::numeric_limits::denorm_min(); } template <> inline double DataInitialization::getValue() { union { uint64_t bits; double value; } x; x.bits = 0x000FFFFFFFFFFFFF; return x.value; } template <> inline double DataInitialization::getValue() { return std::numeric_limits::quiet_NaN(); } template <> inline double DataInitialization::getValue() { return std::numeric_limits::infinity(); } template <> inline double DataInitialization::getValue() { return static_cast((rand() % 2001) - 1000); } template <> inline double DataInitialization::getValue() { return getValue(); } template <> inline double DataInitialization::getValue() { return getValue(); } template <> inline std::complex DataInitialization::getValue, InitMode::Zero>() { return std::complex(0.0f, 0.0f); } template <> inline std::complex DataInitialization::getValue, InitMode::One>() { return std::complex(1.0f, 0.0f); } template <> inline std::complex DataInitialization::getValue, InitMode::Two>() { return std::complex(2.0f, 0.0f); } template <> inline std::complex DataInitialization::getValue, InitMode::NegOne>() { return std::complex(-1.0f, 0.0f); } template <> inline std::complex DataInitialization::getValue, InitMode::Max>() { return std::complex(std::numeric_limits::max(), std::numeric_limits::max()); } template <> inline std::complex DataInitialization::getValue, InitMode::DenormMin>() { return std::complex(std::numeric_limits::denorm_min(), std::numeric_limits::denorm_min()); } template <> inline std::complex DataInitialization::getValue, InitMode::DenormMax>() { union { uint32_t bits; float value; } x; x.bits = 0x007FFFFF; return std::complex(x.value, x.value); } template <> inline std::complex DataInitialization::getValue, InitMode::NaN>() { return std::complex(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); } template <> inline std::complex DataInitialization::getValue, InitMode::Inf>() { return std::complex(std::numeric_limits::infinity(), std::numeric_limits::infinity()); } template <> inline std::complex DataInitialization::getValue, InitMode::Random>() { return std::complex(getValue(), getValue()); } template <> inline std::complex DataInitialization::getValue, InitMode::BadInput>() { return getValue, InitMode::NaN>(); } template <> inline std::complex DataInitialization::getValue, InitMode::BadOutput>() { return getValue, InitMode::Inf>(); } template <> inline std::complex DataInitialization::getValue, InitMode::Zero>() { return std::complex(0.0, 0.0); } template <> inline std::complex DataInitialization::getValue, InitMode::One>() { return std::complex(1.0, 0.0); } template <> inline std::complex DataInitialization::getValue, InitMode::Two>() { return std::complex(2.0, 0.0); } template <> inline std::complex DataInitialization::getValue, InitMode::NegOne>() { return std::complex(-1.0, 0.0); } template <> inline std::complex DataInitialization::getValue, InitMode::Max>() { return std::complex(std::numeric_limits::max(), std::numeric_limits::max()); } template <> inline std::complex DataInitialization::getValue, InitMode::DenormMin>() { return std::complex(std::numeric_limits::denorm_min(), std::numeric_limits::denorm_min()); } template <> inline std::complex DataInitialization::getValue, InitMode::DenormMax>() { union { uint64_t bits; double value; } x; x.bits = 0x000FFFFFFFFFFFFF; return std::complex(x.value, x.value); } template <> inline std::complex DataInitialization::getValue, InitMode::NaN>() { return std::complex(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); } template <> inline std::complex DataInitialization::getValue, InitMode::Inf>() { return std::complex(std::numeric_limits::infinity(), std::numeric_limits::infinity()); } template <> inline std::complex DataInitialization::getValue, InitMode::Random>() { return std::complex(getValue(), getValue()); } template <> inline std::complex DataInitialization::getValue, InitMode::BadInput>() { return getValue, InitMode::NaN>(); } template <> inline std::complex DataInitialization::getValue, InitMode::BadOutput>() { return getValue, InitMode::Inf>(); } template <> inline int32_t DataInitialization::getValue() { return 0; } template <> inline int32_t DataInitialization::getValue() { return 1; } template <> inline int32_t DataInitialization::getValue() { return 2; } template <> inline int32_t DataInitialization::getValue() { return -1; } template <> inline int32_t DataInitialization::getValue() { return std::numeric_limits::max(); } template <> inline int32_t DataInitialization::getValue() { throw std::runtime_error("DenormMin not available for int32_t."); } template <> inline int32_t DataInitialization::getValue() { throw std::runtime_error("DenormMax not available for int32_t."); } template <> inline int32_t DataInitialization::getValue() { throw std::runtime_error("NaN not available for int32_t."); } template <> inline int32_t DataInitialization::getValue() { throw std::runtime_error("Inf not available for int32_t."); } template <> inline int32_t DataInitialization::getValue() { return rand() % 7 - 3; } template <> inline int32_t DataInitialization::getValue() { return std::numeric_limits::max(); } template <> inline int32_t DataInitialization::getValue() { return std::numeric_limits::min(); } template <> inline Int8x4 DataInitialization::getValue() { return Int8x4{0, 0, 0, 0}; } template <> inline Int8x4 DataInitialization::getValue() { return Int8x4{1, 1, 1, 1}; } template <> inline Int8x4 DataInitialization::getValue() { return Int8x4{2, 2, 2, 2}; } template <> inline Int8x4 DataInitialization::getValue() { return Int8x4{-1, -1, -1, -1}; } template <> inline Int8x4 DataInitialization::getValue() { return Int8x4{127, 127, 127, 127}; } template <> inline Int8x4 DataInitialization::getValue() { throw std::runtime_error("DenormMin not available for Int8x4."); } template <> inline Int8x4 DataInitialization::getValue() { throw std::runtime_error("DenormMax not available for Int8x4."); } template <> inline Int8x4 DataInitialization::getValue() { throw std::runtime_error("NaN not available for Int8x4."); } template <> inline Int8x4 DataInitialization::getValue() { throw std::runtime_error("Inf not available for Int8x4."); } template <> inline Int8x4 DataInitialization::getValue() { return Int8x4{static_cast((rand() % 7) - 3), static_cast((rand() % 7) - 3), static_cast((rand() % 7) - 3), static_cast((rand() % 7) - 3)}; } template <> inline Int8x4 DataInitialization::getValue() { auto val = std::numeric_limits::max(); return Int8x4{val, val, val, val}; } template <> inline Int8x4 DataInitialization::getValue() { auto val = std::numeric_limits::min(); return Int8x4{val, val, val, val}; } template <> inline Half DataInitialization::getValue() { return static_cast(0); } template <> inline Half DataInitialization::getValue() { return static_cast(1); } template <> inline Half DataInitialization::getValue() { return static_cast(2); } template <> inline Half DataInitialization::getValue() { return static_cast(-1); } template <> inline Half DataInitialization::getValue() { return static_cast(65504.0); } template <> inline Half DataInitialization::getValue() { union { uint16_t bits; Half value; } x; x.bits = 0x0001; return x.value; } template <> inline Half DataInitialization::getValue() { union { uint16_t bits; Half value; } x; x.bits = 0x03FF; return x.value; } template <> inline Half DataInitialization::getValue() { union { uint16_t bits; Half value; } x; x.bits = 0xFFFF; return x.value; } template <> inline Half DataInitialization::getValue() { union { uint16_t bits; Half value; } x; x.bits = 0x7C00; return x.value; } template <> inline Half DataInitialization::getValue() { return static_cast((rand() % 7) - 3); } template <> inline Half DataInitialization::getValue() { return getValue(); } template <> inline Half DataInitialization::getValue() { return getValue(); } template <> inline BFloat16 DataInitialization::getValue() { return static_cast(0); } template <> inline BFloat16 DataInitialization::getValue() { return static_cast(1); } template <> inline BFloat16 DataInitialization::getValue() { return static_cast(2); } template <> inline BFloat16 DataInitialization::getValue() { return static_cast(-1); } template <> inline BFloat16 DataInitialization::getValue() { return static_cast(DataInitialization::getValue()); } template <> inline BFloat16 DataInitialization::getValue() { BFloat16 bf16; bf16.data = 0x0001; return bf16; } template <> inline BFloat16 DataInitialization::getValue() { BFloat16 bf16; bf16.data = 0x007F; return bf16; } template <> inline BFloat16 DataInitialization::getValue() { return static_cast(std::numeric_limits::quiet_NaN()); } template <> inline BFloat16 DataInitialization::getValue() { return static_cast(std::numeric_limits::infinity()); } template <> inline BFloat16 DataInitialization::getValue() { return static_cast((rand() % 7) - 3); } template <> inline BFloat16 DataInitialization::getValue() { return getValue(); } template <> inline BFloat16 DataInitialization::getValue() { return getValue(); } // Float8 // NOTE: f8/bf8 has special overloading with unit8_t, so using float val and downcast it to f8/bf8 here template <> inline Float8 DataInitialization::getValue() { return static_cast(0.0f); } template <> inline Float8 DataInitialization::getValue() { return static_cast(1.0f); } template <> inline Float8 DataInitialization::getValue() { return static_cast(2.0f); } template <> inline Float8 DataInitialization::getValue() { return static_cast(-1.0f); } template <> inline Float8 DataInitialization::getValue() { // NOTE: sign=0 exp=1111, mantissa=111.. bit pattern : 0x7F //return static_cast(240.0f); union { uint8_t bits; Float8 value; } x; x.bits = 0x7F; return x.value; } template <> inline Float8 DataInitialization::getValue() { // NOTE: sign=0 exp=0000, mantissa=001.. bit pattern : 0x01 //return static_cast(0.0009765625f); union { uint8_t bits; Float8 value; } x; x.bits = 0x01; return x.value; } template <> inline Float8 DataInitialization::getValue() { // NOTE: sign=0 exp=0000, mantissa=111.. bit pattern : 0x07 union { uint8_t bits; Float8 value; } x; x.bits = 0x07; return x.value; } template <> inline Float8 DataInitialization::getValue() { if(!get_hip_f8_bias_mode()) return static_cast(std::numeric_limits::quiet_NaN()); else // optimal mode is unlike IEEE, nan and inf is 0x80 (-0) { union { uint8_t bits; Float8 value; } x; x.bits = 0x80; return x.value; } } template <> inline Float8 DataInitialization::getValue() { if(!get_hip_f8_bias_mode()) return static_cast(std::numeric_limits::infinity()); else // optimal mode is unlike IEEE, nan and inf is 0x80 (-0) { union { uint8_t bits; Float8 value; } x; x.bits = 0x80; return x.value; } } template <> inline Float8 DataInitialization::getValue() { return static_cast((float)((rand() % 7) - 3)); } template <> inline Float8 DataInitialization::getValue() { return getValue(); } template <> inline Float8 DataInitialization::getValue() { // OCP E4M3 (gfx950) has no infinity; use NaN as the guard band. On FNUZ // (gfx942) NaN and Inf share the same bit pattern (0x80), so this is // byte-for-byte unchanged there. isBadOutput matches with isnan. return getValue(); } // BFloat8 template <> inline BFloat8 DataInitialization::getValue() { return static_cast(0.0f); } template <> inline BFloat8 DataInitialization::getValue() { return static_cast(1.0f); } template <> inline BFloat8 DataInitialization::getValue() { return static_cast(2.0f); } template <> inline BFloat8 DataInitialization::getValue() { return static_cast(-1.0f); } template <> inline BFloat8 DataInitialization::getValue() { if(!get_hip_f8_bias_mode()) return static_cast(std::numeric_limits::quiet_NaN()); else // optimal mode is unlike IEEE, nan and inf is 0x80 (-0) { union { uint8_t bits; BFloat8 value; } x; x.bits = 0x80; return x.value; } } template <> inline BFloat8 DataInitialization::getValue() { if(!get_hip_f8_bias_mode()) return static_cast(std::numeric_limits::infinity()); else // optimal mode is unlike IEEE, nan and inf is 0x80 (-0) { union { uint8_t bits; BFloat8 value; } x; x.bits = 0x80; return x.value; } } template <> inline BFloat8 DataInitialization::getValue() { return static_cast((float)((rand() % 7) - 3)); } template <> inline BFloat8 DataInitialization::getValue() { return getValue(); } template <> inline BFloat8 DataInitialization::getValue() { return getValue(); } template <> inline BFloat8 DataInitialization::getValue() { // NOTE: sign=0 exp=11111, mantissa=11.. bit pattern : 0x7F //return static_cast(57344.0f); //return static_cast((uint8_t)0x7F); union { uint8_t bits; BFloat8 value; } x; x.bits = 0x7F; return x.value; } template <> inline BFloat8 DataInitialization::getValue() { // NOTE: sign=0 exp=00000, mantissa=01.. bit pattern : 0x01 //return static_cast(0.0000076294f); union { uint8_t bits; BFloat8 value; } x; x.bits = 0x01; return x.value; } template <> inline BFloat8 DataInitialization::getValue() { // NOTE: sign=0 exp=00000, mantissa=11.. bit pattern : 0x03 union { uint8_t bits; BFloat8 value; } x; x.bits = 0x03; return x.value; } template <> inline int8_t DataInitialization::getValue() { return 0; } template <> inline int8_t DataInitialization::getValue() { return 1; } template <> inline int8_t DataInitialization::getValue() { return 2; } template <> inline int8_t DataInitialization::getValue() { return -1; } template <> inline int8_t DataInitialization::getValue() { return 127; } template <> inline int8_t DataInitialization::getValue() { throw std::runtime_error("DenormMin not available for int8_t."); } template <> inline int8_t DataInitialization::getValue() { throw std::runtime_error("DenormMax not available for int8_t."); } template <> inline int8_t DataInitialization::getValue() { throw std::runtime_error("NaN not available for int8_t."); } template <> inline int8_t DataInitialization::getValue() { throw std::runtime_error("Inf not available for int8_t."); } template <> inline int8_t DataInitialization::getValue() { return static_cast((rand() % 7) - 3); } template <> inline int8_t DataInitialization::getValue() { return std::numeric_limits::max(); } template <> inline int8_t DataInitialization::getValue() { return std::numeric_limits::min(); } template <> inline bool DataInitialization::isBadInput(float value) { return std::isnan(value); } template <> inline bool DataInitialization::isBadInput(double value) { return std::isnan(value); } template <> inline bool DataInitialization::isBadInput>(std::complex value) { return std::isnan(value.real()) && std::isnan(value.imag()); } template <> inline bool DataInitialization::isBadInput>(std::complex value) { return std::isnan(value.real()) && std::isnan(value.imag()); } template <> inline bool DataInitialization::isBadInput(int32_t value) { return value == DataInitialization::getValue(); } template <> inline bool DataInitialization::isBadInput(Int8x4 value) { return value == DataInitialization::getValue(); } template <> inline bool DataInitialization::isBadInput(Half value) { return std::isnan(static_cast(value)); } template <> inline bool DataInitialization::isBadInput(BFloat16 value) { return std::isnan(value); } template <> inline bool DataInitialization::isBadInput(Float8 value) { return std::isnan(value); } template <> inline bool DataInitialization::isBadInput(BFloat8 value) { return std::isnan(value); } template <> inline bool DataInitialization::isBadInput(int8_t value) { return value == DataInitialization::getValue(); } template <> inline bool DataInitialization::isBadOutput(float value) { return std::isinf(value); } template <> inline bool DataInitialization::isBadOutput(double value) { return std::isinf(value); } template <> inline bool DataInitialization::isBadOutput>(std::complex value) { return std::isinf(value.real()) && std::isinf(value.imag()); } template <> inline bool DataInitialization::isBadOutput>(std::complex value) { return std::isinf(value.real()) && std::isinf(value.imag()); } template <> inline bool DataInitialization::isBadOutput(int32_t value) { return value == DataInitialization::getValue(); } template <> inline bool DataInitialization::isBadOutput(Int8x4 value) { return value == DataInitialization::getValue(); } template <> inline bool DataInitialization::isBadOutput(Half value) { return std::isinf(static_cast(value)); } template <> inline bool DataInitialization::isBadOutput(BFloat16 value) { return std::isinf(value); } template <> inline bool DataInitialization::isBadOutput(Float8 value) { // Matches the NaN guard band from getValue. On FNUZ // (gfx942) NaN and Inf are the same bit pattern, so this is unchanged there. return std::isnan(value); } template <> inline bool DataInitialization::isBadOutput(BFloat8 value) { return std::isinf(value); } template <> inline bool DataInitialization::isBadOutput(int8_t value) { return value == DataInitialization::getValue(); } template <> inline float DataInitialization::getTrigValue(int idx, bool useCos, bool useAbs) { float val = useCos ? cos(idx) : sin(idx); if(useAbs) val = abs(val); return val; } template <> inline double DataInitialization::getTrigValue(int idx, bool useCos, bool useAbs) { double val = useCos ? cos(idx) : sin(idx); if(useAbs) val = abs(val); return val; } template <> inline Half DataInitialization::getTrigValue(int idx, bool useCos, bool useAbs) { return static_cast(getTrigValue(idx, useCos, useAbs)); } template <> inline BFloat16 DataInitialization::getTrigValue(int idx, bool useCos, bool useAbs) { return static_cast(getTrigValue(idx, useCos, useAbs)); } template <> inline Float8 DataInitialization::getTrigValue(int idx, bool useCos, bool useAbs) { return static_cast(getTrigValue(idx, useCos, useAbs)); } template <> inline BFloat8 DataInitialization::getTrigValue(int idx, bool useCos, bool useAbs) { return static_cast(getTrigValue(idx, useCos, useAbs)); } template <> inline int32_t DataInitialization::getTrigValue(int idx, bool useCos, bool useAbs) { throw std::runtime_error("Trig not available for int32_t."); } template <> inline Int8x4 DataInitialization::getTrigValue(int idx, bool useCos, bool useAbs) { throw std::runtime_error("Trig not available for Int8x4."); } template <> inline int8_t DataInitialization::getTrigValue(int idx, bool useCos, bool useAbs) { throw std::runtime_error("Trig not available for Int8."); } template <> inline std::complex DataInitialization::getTrigValue>(int idx, bool useCos, bool useAbs) { return std::complex(getTrigValue(idx, useCos, useAbs), getTrigValue(idx, useCos, useAbs)); } template <> inline std::complex DataInitialization::getTrigValue>( int idx, bool useCos, bool useAbs) { return std::complex(getTrigValue(idx, useCos, useAbs), getTrigValue(idx, useCos, useAbs)); } template struct FP_PARAM; template <> struct FP_PARAM { using UINT_T = uint64_t; static constexpr int NUMSIG = 52; static constexpr int NUMEXP = 11; }; template <> struct FP_PARAM { using UINT_T = uint32_t; static constexpr int NUMSIG = 23; static constexpr int NUMEXP = 8; }; template <> struct FP_PARAM { using UINT_T = uint16_t; static constexpr int NUMSIG = 7; static constexpr int NUMEXP = 8; }; template <> struct FP_PARAM { using UINT_T = uint16_t; static constexpr int NUMSIG = 10; static constexpr int NUMEXP = 5; }; template <> struct FP_PARAM { using UINT_T = uint8_t; static constexpr int NUMSIG = 3; static constexpr int NUMEXP = 4; }; template <> struct FP_PARAM { using UINT_T = uint8_t; static constexpr int NUMSIG = 2; static constexpr int NUMEXP = 5; }; template struct rocm_random_common : FP_PARAM { using typename FP_PARAM::UINT_T; using FP_PARAM::NUMSIG; using FP_PARAM::NUMEXP; using random_fp_int_dist = std::uniform_int_distribution; static_assert(sizeof(UINT_T) == sizeof(T), "Type sizes do not match"); static constexpr UINT_T expmask = (((UINT_T)1 << NUMEXP) - 1) << NUMSIG; static constexpr UINT_T expbias = ((UINT_T)1 << (NUMEXP - 1)) - 1; inline static T signsig_exp(UINT_T signsig, UINT_T exp) { union { UINT_T u; T fp; }; u = signsig & ~expmask | ((exp + expbias) << NUMSIG) & expmask; return fp; } }; template <> inline BFloat16 rocm_random_common::signsig_exp(FP_PARAM::UINT_T signsig, FP_PARAM::UINT_T exp) { FP_PARAM::UINT_T u; u = signsig & ~expmask | ((exp + expbias) << NUMSIG) & expmask; return static_cast(u); } template struct rocm_random : rocm_random_common { using typename rocm_random_common::random_fp_int_dist; __attribute__((flatten)) T operator()() { static std::mt19937 rng; int exp = std::uniform_int_distribution{}(rng); exp = exp % (HIGH_EXP - LOW_EXP + 1) + LOW_EXP; return this->signsig_exp(random_fp_int_dist{}(rng), exp); } }; template struct rocm_random_narrow_range; template <> struct rocm_random_narrow_range : rocm_random { }; template <> struct rocm_random_narrow_range : rocm_random { }; template <> struct rocm_random_narrow_range : rocm_random { }; template <> struct rocm_random_narrow_range : rocm_random { }; // TODO: need to check for fp8 and bf8 template <> struct rocm_random_narrow_range : rocm_random { }; template <> struct rocm_random_narrow_range : rocm_random { }; template <> inline float DataInitialization::getValue() { return rocm_random_narrow_range{}(); } template <> inline double DataInitialization::getValue() { return rocm_random_narrow_range{}(); } template <> inline BFloat16 DataInitialization::getValue() { return rocm_random_narrow_range{}(); } template <> inline Half DataInitialization::getValue() { return rocm_random_narrow_range{}(); } template <> inline Float8 DataInitialization::getValue() { return rocm_random_narrow_range{}(); } template <> inline BFloat8 DataInitialization::getValue() { return rocm_random_narrow_range{}(); } template <> inline std::complex DataInitialization::getValue, InitMode::RandomNarrow>() { return std::complex(rocm_random_narrow_range{}(), rocm_random_narrow_range{}()); } template <> inline std::complex DataInitialization::getValue, InitMode::RandomNarrow>() { return std::complex(rocm_random_narrow_range{}(), rocm_random_narrow_range{}()); } template <> inline int32_t DataInitialization::getValue() { return getValue(); } template <> inline Int8x4 DataInitialization::getValue() { return getValue(); } template <> inline int8_t DataInitialization::getValue() { return getValue(); } } // namespace Client } // namespace Tensile