Skip to content

Commit

Permalink
[mlir] Convert raw data in dense element attributes for big-endian ma…
Browse files Browse the repository at this point in the history
…chines.

This patch fixes a bug [[ https://bugs.llvm.org/show_bug.cgi?id=46091 | 46091 ]]

Raw data for the `dense-element attribute` is written in little endian (LE) format.
This commit converts the format to big endian (BE) in ʻAttribute Parser` on the
 BE machine. Also, when outputting on a BE machine, the BE format is converted
 to LE in "AsmPrinter".

Differential Revision: https://reviews.llvm.org/D80695
  • Loading branch information
imaihal authored and River707 committed Oct 29, 2020
1 parent bf0440b commit a66e334
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 2 deletions.
19 changes: 19 additions & 0 deletions mlir/include/mlir/IR/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,25 @@ class DenseIntOrFPElementsAttr
public:
using Base::Base;

/// Convert endianess of input ArrayRef for big-endian(BE) machines. All of
/// the elements of `inRawData` has `type`. If `inRawData` is little endian
/// (LE), it is converted to big endian (BE). Conversely, if `inRawData` is
/// BE, converted to LE.
static void
convertEndianOfArrayRefForBEmachine(ArrayRef<char> inRawData,
MutableArrayRef<char> outRawData,
ShapedType type);

/// Convert endianess of input for big-endian(BE) machines. The number of
/// elements of `inRawData` is `numElements`, and each element has
/// `elementBitWidth` bits. If `inRawData` is little endian (LE), it is
/// converted to big endian (BE) and saved in `outRawData`. Conversely, if
/// `inRawData` is BE, converted to LE.
static void convertEndianOfCharForBEmachine(const char *inRawData,
char *outRawData,
size_t elementBitWidth,
size_t numElements);

protected:
friend DenseElementsAttr;

Expand Down
18 changes: 16 additions & 2 deletions mlir/lib/IR/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1506,8 +1506,22 @@ void ModulePrinter::printDenseIntOrFPElementsAttr(DenseIntOrFPElementsAttr attr,
if (!attr.isSplat() && allowHex &&
shouldPrintElementsAttrWithHex(numElements)) {
ArrayRef<char> rawData = attr.getRawData();
os << '"' << "0x" << llvm::toHex(StringRef(rawData.data(), rawData.size()))
<< "\"";
if (llvm::support::endian::system_endianness() ==
llvm::support::endianness::big) {
// Convert endianess in big-endian(BE) machines. `rawData` is BE in BE
// machines. It is converted here to print in LE format.
SmallVector<char, 64> outDataVec(rawData.size());
MutableArrayRef<char> convRawData(outDataVec);
DenseIntOrFPElementsAttr::convertEndianOfArrayRefForBEmachine(
rawData, convRawData, type);
os << '"' << "0x"
<< llvm::toHex(StringRef(convRawData.data(), convRawData.size()))
<< "\"";
} else {
os << '"' << "0x"
<< llvm::toHex(StringRef(rawData.data(), rawData.size())) << "\"";
}

return;
}

Expand Down
59 changes: 59 additions & 0 deletions mlir/lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,65 @@ DenseIntOrFPElementsAttr::getRawIntOrFloat(ShapedType type, ArrayRef<char> data,
return getRaw(type, data, /*isSplat=*/numElements == 1);
}

void DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(
const char *inRawData, char *outRawData, size_t elementBitWidth,
size_t numElements) {
using llvm::support::ulittle16_t;
using llvm::support::ulittle32_t;
using llvm::support::ulittle64_t;

assert(llvm::support::endian::system_endianness() == // NOLINT
llvm::support::endianness::big); // NOLINT
// NOLINT to avoid warning message about replacing by static_assert()

// Following std::copy_n always converts endianness on BE machine.
switch (elementBitWidth) {
case 16: {
const ulittle16_t *inRawDataPos =
reinterpret_cast<const ulittle16_t *>(inRawData);
uint16_t *outDataPos = reinterpret_cast<uint16_t *>(outRawData);
std::copy_n(inRawDataPos, numElements, outDataPos);
break;
}
case 32: {
const ulittle32_t *inRawDataPos =
reinterpret_cast<const ulittle32_t *>(inRawData);
uint32_t *outDataPos = reinterpret_cast<uint32_t *>(outRawData);
std::copy_n(inRawDataPos, numElements, outDataPos);
break;
}
case 64: {
const ulittle64_t *inRawDataPos =
reinterpret_cast<const ulittle64_t *>(inRawData);
uint64_t *outDataPos = reinterpret_cast<uint64_t *>(outRawData);
std::copy_n(inRawDataPos, numElements, outDataPos);
break;
}
default: {
size_t nBytes = elementBitWidth / CHAR_BIT;
for (size_t i = 0; i < nBytes; i++)
std::copy_n(inRawData + (nBytes - 1 - i), numElements, outRawData + i);
break;
}
}
}

void DenseIntOrFPElementsAttr::convertEndianOfArrayRefForBEmachine(
ArrayRef<char> inRawData, MutableArrayRef<char> outRawData,
ShapedType type) {
size_t numElements = type.getNumElements();
Type elementType = type.getElementType();
if (ComplexType complexTy = elementType.dyn_cast<ComplexType>()) {
elementType = complexTy.getElementType();
numElements = numElements * 2;
}
size_t elementBitWidth = getDenseElementStorageWidth(elementType);
assert(numElements * elementBitWidth == inRawData.size() * CHAR_BIT &&
inRawData.size() <= outRawData.size());
convertEndianOfCharForBEmachine(inRawData.begin(), outRawData.begin(),
elementBitWidth, numElements);
}

//===----------------------------------------------------------------------===//
// DenseFPElementsAttr
//===----------------------------------------------------------------------===//
Expand Down
15 changes: 15 additions & 0 deletions mlir/lib/Parser/AttributeParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/StandardTypes.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Endian.h"

using namespace mlir;
using namespace mlir::detail;
Expand Down Expand Up @@ -696,6 +697,20 @@ DenseElementsAttr TensorLiteralParser::getHexAttr(llvm::SMLoc loc,
return nullptr;
}

if (llvm::support::endian::system_endianness() ==
llvm::support::endianness::big) {
// Convert endianess in big-endian(BE) machines. `rawData` is
// little-endian(LE) because HEX in raw data of dense element attribute
// is always LE format. It is converted into BE here to be used in BE
// machines.
SmallVector<char, 64> outDataVec(rawData.size());
MutableArrayRef<char> convRawData(outDataVec);
DenseIntOrFPElementsAttr::convertEndianOfArrayRefForBEmachine(
rawData, convRawData, type);
return DenseElementsAttr::getFromRawBuffer(type, convRawData,
detectedSplat);
}

return DenseElementsAttr::getFromRawBuffer(type, rawData, detectedSplat);
}

Expand Down
9 changes: 9 additions & 0 deletions mlir/test/IR/dense-elements-hex.mlir
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
// RUN: mlir-opt -allow-unregistered-dialect %s -verify-diagnostics -split-input-file -mlir-print-elementsattrs-with-hex-if-larger=1 | FileCheck %s --check-prefix=HEX
// RUN: mlir-opt -allow-unregistered-dialect %s -verify-diagnostics -split-input-file | FileCheck %s

// HEX: dense<"0x000020410000A040"> : tensor<2xf32>
"foo.op"() {dense.attr = dense<[10.0, 5.0]> : tensor<2xf32>} : () -> ()

// HEX: dense<"0x00000000000024400000000000001440"> : tensor<2xf64>
"foo.op"() {dense.attr = dense<[10.0, 5.0]> : tensor<2xf64>} : () -> ()

// CHECK: dense<[1.000000e+01, 5.000000e+00]> : tensor<2xf32>
"foo.op"() {dense.attr = dense<"0x000020410000A040"> : tensor<2xf32>} : () -> ()

// CHECK: dense<[1.000000e+01, 5.000000e+00]> : tensor<2xf64>
"foo.op"() {dense.attr = dense<"0x00000000000024400000000000001440"> : tensor<2xf64>} : () -> ()

// CHECK: dense<(1.000000e+01,5.000000e+00)> : tensor<2xcomplex<f32>>
"foo.op"() {dense.attr = dense<"0x000020410000A040000020410000A040"> : tensor<2xcomplex<f32>>} : () -> ()

// CHECK: dense<(1.000000e+01,5.000000e+00)> : tensor<2xcomplex<f64>>
"foo.op"() {dense.attr = dense<"0x0000000000002440000000000000144000000000000024400000000000001440"> : tensor<2xcomplex<f64>>} : () -> ()

Expand Down

0 comments on commit a66e334

Please sign in to comment.