Skip to content

Commit fbfbfa5

Browse files
imaihaljoker-eph
authored andcommitted
[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
1 parent 0f9f0a4 commit fbfbfa5

File tree

1 file changed

+94
-24
lines changed

1 file changed

+94
-24
lines changed

mlir/lib/IR/Attributes.cpp

Lines changed: 94 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -567,27 +567,70 @@ static bool getBit(const char *rawData, size_t bitPos) {
567567
return (rawData[bitPos / CHAR_BIT] & (1 << (bitPos % CHAR_BIT))) != 0;
568568
}
569569

570-
/// Get start position of actual data in `value`. Actual data is
571-
/// stored in last `bitWidth`/CHAR_BIT bytes in big endian.
572-
static char *getAPIntDataPos(APInt &value, size_t bitWidth) {
573-
char *dataPos =
574-
const_cast<char *>(reinterpret_cast<const char *>(value.getRawData()));
575-
if (llvm::support::endian::system_endianness() ==
576-
llvm::support::endianness::big)
577-
dataPos = dataPos + 8 - llvm::divideCeil(bitWidth, CHAR_BIT);
578-
return dataPos;
579-
}
580-
581-
/// Read APInt `value` from appropriate position.
582-
static void readAPInt(APInt &value, size_t bitWidth, char *outData) {
583-
char *dataPos = getAPIntDataPos(value, bitWidth);
584-
std::copy_n(dataPos, llvm::divideCeil(bitWidth, CHAR_BIT), outData);
585-
}
586-
587-
/// Write `inData` to appropriate position of APInt `value`.
588-
static void writeAPInt(const char *inData, size_t bitWidth, APInt &value) {
589-
char *dataPos = getAPIntDataPos(value, bitWidth);
590-
std::copy_n(inData, llvm::divideCeil(bitWidth, CHAR_BIT), dataPos);
570+
/// Copy actual `numBytes` data from `value` (APInt) to char array(`result`) for
571+
/// BE format.
572+
static void copyAPIntToArrayForBEmachine(APInt value, size_t numBytes,
573+
char *result) {
574+
assert(llvm::support::endian::system_endianness() == // NOLINT
575+
llvm::support::endianness::big); // NOLINT
576+
assert(value.getNumWords() * APInt::APINT_WORD_SIZE >= numBytes);
577+
578+
// Copy the words filled with data.
579+
// For example, when `value` has 2 words, the first word is filled with data.
580+
// `value` (10 bytes, BE):|abcdefgh|------ij| ==> `result` (BE):|abcdefgh|--|
581+
size_t numFilledWords = (value.getNumWords() - 1) * APInt::APINT_WORD_SIZE;
582+
std::copy_n(reinterpret_cast<const char *>(value.getRawData()),
583+
numFilledWords, result);
584+
// Convert last word of APInt to LE format and store it in char
585+
// array(`valueLE`).
586+
// ex. last word of `value` (BE): |------ij| ==> `valueLE` (LE): |ji------|
587+
size_t lastWordPos = numFilledWords;
588+
SmallVector<char, 8> valueLE(APInt::APINT_WORD_SIZE);
589+
DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(
590+
reinterpret_cast<const char *>(value.getRawData()) + lastWordPos,
591+
valueLE.begin(), APInt::APINT_BITS_PER_WORD, 1);
592+
// Extract actual APInt data from `valueLE`, convert endianness to BE format,
593+
// and store it in `result`.
594+
// ex. `valueLE` (LE): |ji------| ==> `result` (BE): |abcdefgh|ij|
595+
DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(
596+
valueLE.begin(), result + lastWordPos,
597+
(numBytes - lastWordPos) * CHAR_BIT, 1);
598+
}
599+
600+
/// Copy `numBytes` data from `inArray`(char array) to `result`(APINT) for BE
601+
/// format.
602+
static void copyArrayToAPIntForBEmachine(const char *inArray, size_t numBytes,
603+
APInt &result) {
604+
assert(llvm::support::endian::system_endianness() == // NOLINT
605+
llvm::support::endianness::big); // NOLINT
606+
assert(result.getNumWords() * APInt::APINT_WORD_SIZE >= numBytes);
607+
608+
// Copy the data that fills the word of `result` from `inArray`.
609+
// For example, when `result` has 2 words, the first word will be filled with
610+
// data. So, the first 8 bytes are copied from `inArray` here.
611+
// `inArray` (10 bytes, BE): |abcdefgh|ij|
612+
// ==> `result` (2 words, BE): |abcdefgh|--------|
613+
size_t numFilledWords = (result.getNumWords() - 1) * APInt::APINT_WORD_SIZE;
614+
std::copy_n(
615+
inArray, numFilledWords,
616+
const_cast<char *>(reinterpret_cast<const char *>(result.getRawData())));
617+
618+
// Convert array data which will be last word of `result` to LE format, and
619+
// store it in char array(`inArrayLE`).
620+
// ex. `inArray` (last two bytes, BE): |ij| ==> `inArrayLE` (LE): |ji------|
621+
size_t lastWordPos = numFilledWords;
622+
SmallVector<char, 8> inArrayLE(APInt::APINT_WORD_SIZE);
623+
DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(
624+
inArray + lastWordPos, inArrayLE.begin(),
625+
(numBytes - lastWordPos) * CHAR_BIT, 1);
626+
627+
// Convert `inArrayLE` to BE format, and store it in last word of `result`.
628+
// ex. `inArrayLE` (LE): |ji------| ==> `result` (BE): |abcdefgh|------ij|
629+
DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(
630+
inArrayLE.begin(),
631+
const_cast<char *>(reinterpret_cast<const char *>(result.getRawData())) +
632+
lastWordPos,
633+
APInt::APINT_BITS_PER_WORD, 1);
591634
}
592635

593636
/// Writes value to the bit position `bitPos` in array `rawData`.
@@ -600,7 +643,20 @@ static void writeBits(char *rawData, size_t bitPos, APInt value) {
600643

601644
// Otherwise, the bit position is guaranteed to be byte aligned.
602645
assert((bitPos % CHAR_BIT) == 0 && "expected bitPos to be 8-bit aligned");
603-
readAPInt(value, bitWidth, rawData + (bitPos / CHAR_BIT));
646+
if (llvm::support::endian::system_endianness() ==
647+
llvm::support::endianness::big) {
648+
// Copy from `value` to `rawData + (bitPos / CHAR_BIT)`.
649+
// Copying the first `llvm::divideCeil(bitWidth, CHAR_BIT)` bytes doesn't
650+
// work correctly in BE format.
651+
// ex. `value` (2 words including 10 bytes)
652+
// ==> BE: |abcdefgh|------ij|, LE: |hgfedcba|ji------|
653+
copyAPIntToArrayForBEmachine(value, llvm::divideCeil(bitWidth, CHAR_BIT),
654+
rawData + (bitPos / CHAR_BIT));
655+
} else {
656+
std::copy_n(reinterpret_cast<const char *>(value.getRawData()),
657+
llvm::divideCeil(bitWidth, CHAR_BIT),
658+
rawData + (bitPos / CHAR_BIT));
659+
}
604660
}
605661

606662
/// 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) {
613669
// Otherwise, the bit position must be 8-bit aligned.
614670
assert((bitPos % CHAR_BIT) == 0 && "expected bitPos to be 8-bit aligned");
615671
APInt result(bitWidth, 0);
616-
writeAPInt(rawData + (bitPos / CHAR_BIT), bitWidth, result);
672+
if (llvm::support::endian::system_endianness() ==
673+
llvm::support::endianness::big) {
674+
// Copy from `rawData + (bitPos / CHAR_BIT)` to `result`.
675+
// Copying the first `llvm::divideCeil(bitWidth, CHAR_BIT)` bytes doesn't
676+
// work correctly in BE format.
677+
// ex. `result` (2 words including 10 bytes)
678+
// ==> BE: |abcdefgh|------ij|, LE: |hgfedcba|ji------| This function
679+
copyArrayToAPIntForBEmachine(rawData + (bitPos / CHAR_BIT),
680+
llvm::divideCeil(bitWidth, CHAR_BIT), result);
681+
} else {
682+
std::copy_n(rawData + (bitPos / CHAR_BIT),
683+
llvm::divideCeil(bitWidth, CHAR_BIT),
684+
const_cast<char *>(
685+
reinterpret_cast<const char *>(result.getRawData())));
686+
}
617687
return result;
618688
}
619689

@@ -1175,7 +1245,7 @@ void DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(
11751245
default: {
11761246
size_t nBytes = elementBitWidth / CHAR_BIT;
11771247
for (size_t i = 0; i < nBytes; i++)
1178-
std::copy_n(inRawData + (nBytes - 1 - i), numElements, outRawData + i);
1248+
std::copy_n(inRawData + (nBytes - 1 - i), 1, outRawData + i);
11791249
break;
11801250
}
11811251
}

0 commit comments

Comments
 (0)