// Copyright Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #pragma once #include #include #include #include #include #include #include #include #include #include namespace rocRoller { struct AppendKernelArgumentsVisitor { KernelArguments* args; std::string const& argName; template void operator()(T const& value) { args->append(argName, value); } void call(CommandArgumentValue const& value) { std::visit(*this, value); } }; template inline void KernelArguments::append(std::string const& argName, T value) { if constexpr(std::is_same_v) { AppendKernelArgumentsVisitor visitor{this, argName}; visitor.call(value); } else { append(argName, value, true); } } template inline void KernelArguments::appendUnbound(std::string const& argName) { append(argName, static_cast(0), false); } template inline void KernelArguments::bind(std::string const& argName, T value) { if(!m_log) { throw std::runtime_error("Binding is not supported without logging."); } auto it = m_argRecords.find(argName); if(it == m_argRecords.end()) { throw std::runtime_error("Attempt to bind unknown argument " + argName); } auto& record = it->second; if(std::get(record)) { throw std::runtime_error("Attempt to bind already bound argument " + argName); } if(sizeof(T) != std::get(record)) { throw std::runtime_error("Size mismatch in binding argument " + argName); } size_t offset = std::get(record); if(offset % alignof(T) != 0) { throw std::runtime_error("Alignment error in argument " + argName + ": type mismatch?"); } writeValue(offset, value); std::get(record) = stringForValue(value, true); std::get(record) = true; } template inline std::string KernelArguments::stringForValue(T value, bool bound) const { if(!m_log) return ""; if(!bound) return ""; std::ostringstream msg; // Some pointer types (char, int8_t, uint8_t, etc) are interpreted // as a string, we just want the pointer value. if constexpr(std::is_pointer_v) { msg << static_cast(value); } else { msg << value; } return msg.str(); } template inline void KernelArguments::append(std::string const& argName, T value, bool bound) { auto constexpr PACKED_SCALE_ALIGN = alignof(typename PackedTypeOf::type); auto constexpr PACKED_SCALE_SIZE = sizeof(typename PackedTypeOf::type); alignTo(PACKED_SCALE_ALIGN); size_t offset = m_data.size(); size_t argSize = PACKED_SCALE_SIZE; if(m_log) { std::string valueString = stringForValue(value, bound); appendRecord(argName, Arg(offset, argSize, bound, valueString)); } m_data.insert(m_data.end(), argSize, 0); writeValue(offset, value); } template inline void KernelArguments::writeValue(size_t offset, T value) { auto constexpr PACKED_SCALE_SIZE = sizeof(typename PackedTypeOf::type); size_t argSize = PACKED_SCALE_SIZE; AssertFatal(offset + argSize <= m_data.size(), "Value exceeds allocated bounds.", ShowValue(offset), ShowValue(argSize), ShowValue(m_data.size())); std::memset(&m_data[offset], value.scale, argSize); } template inline void KernelArguments::append(std::string const& argName, T value, bool bound) { alignTo(alignof(T)); size_t offset = m_data.size(); size_t argSize = sizeof(T); if(m_log) { std::string valueString = stringForValue(value, bound); appendRecord(argName, Arg(offset, argSize, bound, valueString)); } m_data.insert(m_data.end(), argSize, 0); writeValue(offset, value); } template inline void KernelArguments::writeValue(size_t offset, T value) { AssertFatal(offset + sizeof(T) <= m_data.size(), "Value exceeds allocated bounds.", ShowValue(offset), ShowValue(sizeof(T)), ShowValue(m_data.size())); std::memcpy(&m_data[offset], &value, sizeof(T)); } inline void KernelArguments::alignTo(size_t alignment) { size_t extraElements = m_data.size() % alignment; size_t padding = (alignment - extraElements) % alignment; m_data.insert(m_data.end(), padding, 0); } inline void KernelArguments::appendRecord(std::string const& argName, KernelArguments::Arg record) { auto it = m_argRecords.find(argName); if(it != m_argRecords.end()) { throw std::runtime_error("Duplicate argument name: " + argName); } m_argRecords[argName] = record; m_names.push_back(argName); } template KernelArguments::const_iterator::operator T() const { if(sizeof(T) != m_value.second) { throw std::bad_cast(); } return *reinterpret_cast(const_cast(m_value.first)); } inline bool KernelArguments::log() const { return m_log; } inline std::vector const& KernelArguments::dataVector() const { return m_data; } inline RuntimeArguments KernelArguments::runtimeArguments() const { return RuntimeArguments(m_data.data(), m_data.size()); } }