Index: llvm/include/llvm/DebugInfo/MSF/MSFCommon.h =================================================================== --- llvm/include/llvm/DebugInfo/MSF/MSFCommon.h +++ llvm/include/llvm/DebugInfo/MSF/MSFCommon.h @@ -52,6 +52,16 @@ struct MSFLayout { MSFLayout() = default; + uint32_t mainFpmBlock() const { + assert(SB->FreeBlockMapBlock == 1 || SB->FreeBlockMapBlock == 2); + return SB->FreeBlockMapBlock; + } + + uint32_t alternateFpmBlock() const { + // If mainFpmBlock is 1, this is 2. If mainFpmBlock is 2, this is 1. + return 3U - mainFpmBlock(); + } + const SuperBlock *SB = nullptr; BitVector FreePageMap; ArrayRef DirectoryBlocks; @@ -108,14 +118,40 @@ return L.SB->BlockSize; } -inline uint32_t getNumFpmIntervals(const MSFLayout &L, - bool IncludeUnusedFpmData = false) { - if (IncludeUnusedFpmData) - return divideCeil(L.SB->NumBlocks, L.SB->BlockSize); +/// Given an MSF with the specified block size and number of blocks, determine +/// how many pieces the specified Fpm is split into. +/// \p BlockSize - the block size of the MSF +/// \p NumBlocks - the total number of blocks in the MSF +/// \p IncludeUnusedFpmData - When true, this will count every block that is +/// both in the file and matches the form of an FPM block, even if some of +/// those FPM blocks are unused (a single FPM block can describe the +/// allocation status of up to 32,767 blocks, although one appears only +/// every 4,096 blocks). So there are 8x as many blocks that match the +/// form as there are blocks that are necessary to describe the allocation +/// status of the file. When this parameter is false, these extraneous +/// trailing blocks are not counted. +inline uint32_t getNumFpmIntervals(uint32_t BlockSize, uint32_t NumBlocks, + bool IncludeUnusedFpmData, int FpmNumber) { + assert(FpmNumber == 1 || FpmNumber == 2); + if (IncludeUnusedFpmData) { + // This calculation determines how many times a number of the form + // BlockSize * k + N appears in the range [0, NumBlocks). We only need to + // do this when unused data is included, since the number of blocks dwarfs + // the number of fpm blocks. + return divideCeil(NumBlocks - FpmNumber, BlockSize); + } // We want the minimum number of intervals required, where each interval can // represent BlockSize * 8 blocks. - return divideCeil(L.SB->NumBlocks, 8 * L.SB->BlockSize); + return divideCeil(NumBlocks, 8 * BlockSize); +} + +inline uint32_t getNumFpmIntervals(const MSFLayout &L, + bool IncludeUnusedFpmData = false, + bool AltFpm = false) { + return getNumFpmIntervals(L.SB->BlockSize, L.SB->NumBlocks, + IncludeUnusedFpmData, + AltFpm ? L.alternateFpmBlock() : L.mainFpmBlock()); } Error validateSuperBlock(const SuperBlock &SB); Index: llvm/lib/DebugInfo/MSF/MSFCommon.cpp =================================================================== --- llvm/lib/DebugInfo/MSF/MSFCommon.cpp +++ llvm/lib/DebugInfo/MSF/MSFCommon.cpp @@ -64,15 +64,13 @@ bool IncludeUnusedFpmData, bool AltFpm) { MSFStreamLayout FL; - uint32_t NumFpmIntervals = getNumFpmIntervals(Msf, IncludeUnusedFpmData); - support::ulittle32_t FpmBlock = Msf.SB->FreeBlockMapBlock; - assert(FpmBlock == 1 || FpmBlock == 2); - if (AltFpm) { - // If they requested the alternate FPM, then 2 becomes 1 and 1 becomes 2. - FpmBlock = 3U - FpmBlock; - } + uint32_t NumFpmIntervals = + getNumFpmIntervals(Msf, IncludeUnusedFpmData, AltFpm); + + uint32_t FpmBlock = AltFpm ? Msf.alternateFpmBlock() : Msf.mainFpmBlock(); + for (uint32_t I = 0; I < NumFpmIntervals; ++I) { - FL.Blocks.push_back(FpmBlock); + FL.Blocks.push_back(support::ulittle32_t(FpmBlock)); FpmBlock += msf::getFpmIntervalLength(Msf); } Index: llvm/unittests/DebugInfo/MSF/MSFCommonTest.cpp =================================================================== --- llvm/unittests/DebugInfo/MSF/MSFCommonTest.cpp +++ llvm/unittests/DebugInfo/MSF/MSFCommonTest.cpp @@ -46,12 +46,47 @@ EXPECT_EQ(1u, getNumFpmIntervals(L, true)); SB.NumBlocks = SB.BlockSize; EXPECT_EQ(1u, getNumFpmIntervals(L, true)); - SB.NumBlocks = SB.BlockSize + 1; - EXPECT_EQ(2u, getNumFpmIntervals(L, true)); SB.NumBlocks = SB.BlockSize * 8; EXPECT_EQ(8u, getNumFpmIntervals(L, true)); - SB.NumBlocks = SB.BlockSize * 8 + 1; - EXPECT_EQ(9u, getNumFpmIntervals(L, true)); + + // The FPM is going to look like this: + // | 0 | 1 | 2 | ... | 4096 | 4097 | 4098 | ... | + // | SB | FPM0 | FPM1 | Data | Data | FPM0 | FPM1 | ... | + // + // So when there are up to 4097 blocks (last index 4096), the final blocks + // are data blocks. When there are 4098 blocks (last index 4097), there is + // one terminating FPM block, and when there are 4099 blocks, there are two + // terminating FPM blocks. Make sure all these cases are handled. + + // With 4096 or 4097 blocks, the last block is a data block so we only have + // 1 interval. + for (uint32_t I : {4096, 4097}) { + // 1 FPM0 interval + EXPECT_EQ(1U, getNumFpmIntervals(4096, I, true, 1)); + EXPECT_EQ(1U, getNumFpmIntervals(4096, I, false, 1)); + + // 1 FPM1 interval + EXPECT_EQ(1U, getNumFpmIntervals(4096, I, true, 2)); + EXPECT_EQ(1U, getNumFpmIntervals(4096, I, false, 2)); + } + + // With 4098 blocks, the last block belongs to FPM0 so we should have 2 FPM0 + // intervals. + EXPECT_EQ(2U, getNumFpmIntervals(4096, 4098, true, 1)); + EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, false, 1)); + + // And 1 FPM1 interval. + EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, true, 2)); + EXPECT_EQ(1U, getNumFpmIntervals(4096, 4098, false, 2)); + + // With 4099 blocks, the last block belongs to FPM1 so we should have 2 + // FPM0 intervals. + EXPECT_EQ(2U, getNumFpmIntervals(4096, 4099, true, 1)); + EXPECT_EQ(1U, getNumFpmIntervals(4096, 4099, false, 1)); + + // And 2 FPM1 intervals. + EXPECT_EQ(2U, getNumFpmIntervals(4096, 4099, true, 2)); + EXPECT_EQ(1U, getNumFpmIntervals(4096, 4099, false, 2)); } TEST(MSFCommonTest, FpmStreamLayout) { @@ -95,7 +130,7 @@ // 2. When we are including unused FPM data, there should be one FPM block // at every BlockSize interval in the file, even if entire FPM blocks are // unused. - SB.NumBlocks = SB.BlockSize * 8 + 1; + SB.NumBlocks = SB.BlockSize * 8 + 3; SL = getFpmStreamLayout(L, true, false); EXPECT_EQ(SB.BlockSize * 9, SL.Length); EXPECT_EQ(9u, SL.Blocks.size());