From fbfbfa5c713f4b98f174f9ff3416eaf42d133bc0 Mon Sep 17 00:00:00 2001 From: Haruki Imai Date: Fri, 20 Nov 2020 05:07:31 +0000 Subject: [PATCH] [mlir] Support big-endian systems in DenseElementsAttr (multiple word) D78076 supports big endian in DenseElementsAttr, but does not work when APInt has multiple words(the number of bits > 64). This patch updates D78076 to support it. This patch removed the fix in D78076 and re-implemented to support multiple words. Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D80272 --- mlir/lib/IR/Attributes.cpp | 118 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 94 insertions(+), 24 deletions(-) diff --git a/mlir/lib/IR/Attributes.cpp b/mlir/lib/IR/Attributes.cpp index 354f6cc..277607a 100644 --- a/mlir/lib/IR/Attributes.cpp +++ b/mlir/lib/IR/Attributes.cpp @@ -567,27 +567,70 @@ static bool getBit(const char *rawData, size_t bitPos) { return (rawData[bitPos / CHAR_BIT] & (1 << (bitPos % CHAR_BIT))) != 0; } -/// Get start position of actual data in `value`. Actual data is -/// stored in last `bitWidth`/CHAR_BIT bytes in big endian. -static char *getAPIntDataPos(APInt &value, size_t bitWidth) { - char *dataPos = - const_cast(reinterpret_cast(value.getRawData())); - if (llvm::support::endian::system_endianness() == - llvm::support::endianness::big) - dataPos = dataPos + 8 - llvm::divideCeil(bitWidth, CHAR_BIT); - return dataPos; -} - -/// Read APInt `value` from appropriate position. -static void readAPInt(APInt &value, size_t bitWidth, char *outData) { - char *dataPos = getAPIntDataPos(value, bitWidth); - std::copy_n(dataPos, llvm::divideCeil(bitWidth, CHAR_BIT), outData); -} - -/// Write `inData` to appropriate position of APInt `value`. -static void writeAPInt(const char *inData, size_t bitWidth, APInt &value) { - char *dataPos = getAPIntDataPos(value, bitWidth); - std::copy_n(inData, llvm::divideCeil(bitWidth, CHAR_BIT), dataPos); +/// Copy actual `numBytes` data from `value` (APInt) to char array(`result`) for +/// BE format. +static void copyAPIntToArrayForBEmachine(APInt value, size_t numBytes, + char *result) { + assert(llvm::support::endian::system_endianness() == // NOLINT + llvm::support::endianness::big); // NOLINT + assert(value.getNumWords() * APInt::APINT_WORD_SIZE >= numBytes); + + // Copy the words filled with data. + // For example, when `value` has 2 words, the first word is filled with data. + // `value` (10 bytes, BE):|abcdefgh|------ij| ==> `result` (BE):|abcdefgh|--| + size_t numFilledWords = (value.getNumWords() - 1) * APInt::APINT_WORD_SIZE; + std::copy_n(reinterpret_cast(value.getRawData()), + numFilledWords, result); + // Convert last word of APInt to LE format and store it in char + // array(`valueLE`). + // ex. last word of `value` (BE): |------ij| ==> `valueLE` (LE): |ji------| + size_t lastWordPos = numFilledWords; + SmallVector valueLE(APInt::APINT_WORD_SIZE); + DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine( + reinterpret_cast(value.getRawData()) + lastWordPos, + valueLE.begin(), APInt::APINT_BITS_PER_WORD, 1); + // Extract actual APInt data from `valueLE`, convert endianness to BE format, + // and store it in `result`. + // ex. `valueLE` (LE): |ji------| ==> `result` (BE): |abcdefgh|ij| + DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine( + valueLE.begin(), result + lastWordPos, + (numBytes - lastWordPos) * CHAR_BIT, 1); +} + +/// Copy `numBytes` data from `inArray`(char array) to `result`(APINT) for BE +/// format. +static void copyArrayToAPIntForBEmachine(const char *inArray, size_t numBytes, + APInt &result) { + assert(llvm::support::endian::system_endianness() == // NOLINT + llvm::support::endianness::big); // NOLINT + assert(result.getNumWords() * APInt::APINT_WORD_SIZE >= numBytes); + + // Copy the data that fills the word of `result` from `inArray`. + // For example, when `result` has 2 words, the first word will be filled with + // data. So, the first 8 bytes are copied from `inArray` here. + // `inArray` (10 bytes, BE): |abcdefgh|ij| + // ==> `result` (2 words, BE): |abcdefgh|--------| + size_t numFilledWords = (result.getNumWords() - 1) * APInt::APINT_WORD_SIZE; + std::copy_n( + inArray, numFilledWords, + const_cast(reinterpret_cast(result.getRawData()))); + + // Convert array data which will be last word of `result` to LE format, and + // store it in char array(`inArrayLE`). + // ex. `inArray` (last two bytes, BE): |ij| ==> `inArrayLE` (LE): |ji------| + size_t lastWordPos = numFilledWords; + SmallVector inArrayLE(APInt::APINT_WORD_SIZE); + DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine( + inArray + lastWordPos, inArrayLE.begin(), + (numBytes - lastWordPos) * CHAR_BIT, 1); + + // Convert `inArrayLE` to BE format, and store it in last word of `result`. + // ex. `inArrayLE` (LE): |ji------| ==> `result` (BE): |abcdefgh|------ij| + DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine( + inArrayLE.begin(), + const_cast(reinterpret_cast(result.getRawData())) + + lastWordPos, + APInt::APINT_BITS_PER_WORD, 1); } /// Writes value to the bit position `bitPos` in array `rawData`. @@ -600,7 +643,20 @@ static void writeBits(char *rawData, size_t bitPos, APInt value) { // Otherwise, the bit position is guaranteed to be byte aligned. assert((bitPos % CHAR_BIT) == 0 && "expected bitPos to be 8-bit aligned"); - readAPInt(value, bitWidth, rawData + (bitPos / CHAR_BIT)); + if (llvm::support::endian::system_endianness() == + llvm::support::endianness::big) { + // Copy from `value` to `rawData + (bitPos / CHAR_BIT)`. + // Copying the first `llvm::divideCeil(bitWidth, CHAR_BIT)` bytes doesn't + // work correctly in BE format. + // ex. `value` (2 words including 10 bytes) + // ==> BE: |abcdefgh|------ij|, LE: |hgfedcba|ji------| + copyAPIntToArrayForBEmachine(value, llvm::divideCeil(bitWidth, CHAR_BIT), + rawData + (bitPos / CHAR_BIT)); + } else { + std::copy_n(reinterpret_cast(value.getRawData()), + llvm::divideCeil(bitWidth, CHAR_BIT), + rawData + (bitPos / CHAR_BIT)); + } } /// Reads the next `bitWidth` bits from the bit position `bitPos` in array @@ -613,7 +669,21 @@ static APInt readBits(const char *rawData, size_t bitPos, size_t bitWidth) { // Otherwise, the bit position must be 8-bit aligned. assert((bitPos % CHAR_BIT) == 0 && "expected bitPos to be 8-bit aligned"); APInt result(bitWidth, 0); - writeAPInt(rawData + (bitPos / CHAR_BIT), bitWidth, result); + if (llvm::support::endian::system_endianness() == + llvm::support::endianness::big) { + // Copy from `rawData + (bitPos / CHAR_BIT)` to `result`. + // Copying the first `llvm::divideCeil(bitWidth, CHAR_BIT)` bytes doesn't + // work correctly in BE format. + // ex. `result` (2 words including 10 bytes) + // ==> BE: |abcdefgh|------ij|, LE: |hgfedcba|ji------| This function + copyArrayToAPIntForBEmachine(rawData + (bitPos / CHAR_BIT), + llvm::divideCeil(bitWidth, CHAR_BIT), result); + } else { + std::copy_n(rawData + (bitPos / CHAR_BIT), + llvm::divideCeil(bitWidth, CHAR_BIT), + const_cast( + reinterpret_cast(result.getRawData()))); + } return result; } @@ -1175,7 +1245,7 @@ void DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine( 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); + std::copy_n(inRawData + (nBytes - 1 - i), 1, outRawData + i); break; } } -- 2.7.4