blob: 6f4cd1a120947ccefc37538e69fee0b472e6347f [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*!
* \file codegen_params.cc
*/
#include "codegen_params.h"
#include <dlpack/dlpack.h>
#include <cmath>
#include <iomanip>
#include <memory>
#include <string>
namespace tvm {
namespace codegen {
/*! \brief maximum line length of generated parameters, including indent. */
static constexpr const int kMaxLineLength = 80;
static int ComputeNumElementsPerRow(int one_element_size_bytes, int indent_chars) {
if (one_element_size_bytes > kMaxLineLength - indent_chars) {
return 1;
}
// When multiple elements fit per line, divide the available space by the size of one element,
// and return the largest power of 2 less than the result. Using power-of-2-sized elements allows
// for easily traversing the generated code.
int elements_per_row = (kMaxLineLength - indent_chars) / one_element_size_bytes;
// Implementation of fls. Iteratively clear the LSB until one bit remains.
while ((elements_per_row & (elements_per_row - 1)) > 0) {
elements_per_row &= elements_per_row - 1;
}
return elements_per_row;
}
template <typename T, typename Enable = std::enable_if<std::is_integral<T>::value>>
void PrintIntegralArray(void* data, size_t num_elements, int indent_chars, std::ostream& os,
const std::string& eol) {
int one_element_size_bytes = (sizeof(T) / 4) + (2 /* "0x" */) + (2 /* ", " */);
if (std::is_signed<T>::value) {
one_element_size_bytes += 1; // sign character
if (sizeof(T) == 64 / 8) {
one_element_size_bytes += 2; // "LL"
}
} else {
if (sizeof(T) == 64 / 8) {
one_element_size_bytes += 3; // "ULL"
}
}
size_t elements_per_row = ComputeNumElementsPerRow(one_element_size_bytes, indent_chars);
std::string indent_str(indent_chars, ' ');
for (size_t i = 0; i < num_elements; i++) {
if ((i % elements_per_row) == 0) {
os << indent_str;
}
int64_t elem = static_cast<T*>(data)[i];
if (std::is_signed<T>::value) {
uint64_t to_print;
if (elem < 0) {
os << "-";
to_print = -elem;
} else {
os << "+";
to_print = elem;
}
os << "0x" << std::setw(sizeof(T) * 8 / 4) << static_cast<std::uint64_t>(to_print);
if (sizeof(T) == 64 / 8) {
os << "LL";
}
} else {
os << "0x" << std::setw(sizeof(T) * 8 / 4) << static_cast<std::uint64_t>(elem);
if (sizeof(T) == 64 / 8) {
os << "ULL";
}
}
if (i < num_elements - 1) {
os << ", ";
}
if ((i % elements_per_row) == elements_per_row - 1) {
os << eol;
}
}
if ((num_elements % elements_per_row) != 0) {
os << eol;
}
}
template <typename T, typename Enable = std::enable_if<std::is_floating_point<T>::value>>
void PrintFloatingPointArray(void* data, size_t num_elements, int indent_chars, std::ostream& os,
const std::string& eol) {
// Floats and doubles are printed as hex but casted.
int one_element_size_bytes = (sizeof(T) / 4) + (2 /* "0x" */) + (2 /* ", " */) + 1 /* sign */ +
1 /* decimal point */ + 1 /* exponent sign */;
if (sizeof(T) == 64 / 8) {
one_element_size_bytes += 2; /* 4 decimal digits in exponent, relative to bits / 4 */
} else if (sizeof(T) == 32 / 8) {
one_element_size_bytes += 1; /* extra decimal digit in exponent, relative to bits / 4 */
}
size_t elements_per_row = ComputeNumElementsPerRow(one_element_size_bytes, indent_chars);
std::string indent_str(indent_chars, ' ');
std::stringstream ss;
if (std::is_signed<T>::value) {
ss.setf(std::ios::hex | std::ios::showbase | std::ios::fixed | std::ios::scientific,
std::ios::basefield | std::ios::showbase | std::ios::floatfield);
} else {
ss.setf(std::ios::hex | std::ios::fixed | std::ios::scientific,
std::ios::basefield | std::ios::showbase | std::ios::floatfield);
}
for (size_t i = 0; i < num_elements; i++) {
if ((i % elements_per_row) == 0) {
os << indent_str;
}
T elem = static_cast<T*>(data)[i];
if (std::isinf(elem)) {
// C99 standard.
os << (elem < 0 ? "-" : " ") << std::setw(one_element_size_bytes - 1) << "INFINITY";
} else if (std::isnan(elem)) {
// GNU extension, implemenatation-dependent.
os << std::setw(one_element_size_bytes) << "NAN";
} else {
ss << elem;
os << std::setw(one_element_size_bytes) << ss.str();
ss.str("");
}
if (i < num_elements - 1) {
os << ", ";
}
if ((i % elements_per_row) == elements_per_row - 1) {
os << eol;
}
}
if ((num_elements % elements_per_row) != 0) {
os << eol;
}
}
void TensorDataToC(::tvm::runtime::Tensor arr, int indent_chars, std::ostream& os,
const std::string& eol) {
auto arr_type = arr.DataType();
TVM_FFI_ICHECK_EQ(arr_type.lanes, 1)
<< "CodegenParams: only support generating 1-lane parameters; saw " << arr_type.lanes;
auto shape = arr.Shape();
int num_elements = 1;
for (auto shape_elem : shape) {
num_elements *= shape_elem;
}
auto old_fmtflags = os.flags();
os.setf(std::ios::internal | std::ios::hex,
std::ios::adjustfield | std::ios::basefield | std::ios::showbase);
os.fill('0');
switch (static_cast<DLDataTypeCode>(arr_type.code)) {
case DLDataTypeCode::kDLInt:
TVM_FFI_ICHECK(arr_type.bits == 8 || arr_type.bits == 16 || arr_type.bits == 32 ||
arr_type.bits == 64)
<< "CodegenParams: only support generating 8-, 16-, 32-, or 64-bit integer params; saw "
<< arr_type.bits << "-bit array";
if (arr_type.bits == 8) {
PrintIntegralArray<int8_t>(arr->data, num_elements, indent_chars, os, eol);
} else if (arr_type.bits == 16) {
PrintIntegralArray<int16_t>(arr->data, num_elements, indent_chars, os, eol);
} else if (arr_type.bits == 32) {
PrintIntegralArray<int32_t>(arr->data, num_elements, indent_chars, os, eol);
} else if (arr_type.bits == 64) {
PrintIntegralArray<int64_t>(arr->data, num_elements, indent_chars, os, eol);
} else {
TVM_FFI_ICHECK(false) << "should not get here";
}
break;
case DLDataTypeCode::kDLUInt:
TVM_FFI_ICHECK(arr_type.bits == 8 || arr_type.bits == 16 || arr_type.bits == 32 ||
arr_type.bits == 64)
<< "CodegenParams: only support generating 8-, 16-, 32-, or 64-bit integer params; saw "
<< arr_type.bits << "-bit array";
if (arr_type.bits == 8) {
PrintIntegralArray<uint8_t>(arr->data, num_elements, indent_chars, os, eol);
} else if (arr_type.bits == 16) {
PrintIntegralArray<uint16_t>(arr->data, num_elements, indent_chars, os, eol);
} else if (arr_type.bits == 32) {
PrintIntegralArray<uint32_t>(arr->data, num_elements, indent_chars, os, eol);
} else if (arr_type.bits == 64) {
PrintIntegralArray<uint64_t>(arr->data, num_elements, indent_chars, os, eol);
} else {
TVM_FFI_ICHECK(false) << "should not get here";
}
break;
case DLDataTypeCode::kDLFloat: {
os.fill(' ');
os.setf(std::ios::left, std::ios::adjustfield);
if (arr_type.bits == 16) {
// NOTE: print types not widely supported by C as uint16_t.
PrintIntegralArray<uint16_t>(arr->data, num_elements, indent_chars, os, eol);
} else if (arr_type.bits == 32) {
PrintFloatingPointArray<float>(arr->data, num_elements, indent_chars, os, eol);
} else if (arr_type.bits == 64) {
PrintFloatingPointArray<double>(arr->data, num_elements, indent_chars, os, eol);
} else {
TVM_FFI_ICHECK(false) << "CodegenParams: only support 32- or 64-bit floating point; saw "
<< arr_type.bits << "-bit array";
}
break;
}
case DLDataTypeCode::kDLBfloat: {
// NOTE: print types not widely supported by C as uint16_t.
TVM_FFI_ICHECK(arr_type.bits == 16)
<< "CodegenParams: only support generating 16-bit bfloat params; saw " << arr_type.bits
<< "-bit array";
PrintIntegralArray<uint16_t>(arr->data, num_elements, indent_chars, os, eol);
break;
}
default:
TVM_FFI_ICHECK(false) << "Data type '" << ffi::DLDataTypeToString(arr_type)
<< "' not supported";
}
os.flags(old_fmtflags);
}
} // namespace codegen
} // namespace tvm