1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/reorder/basic_block_optimizer.h"
16 :
17 : #include "gmock/gmock.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/block_graph/basic_block_test_util.h"
20 : #include "syzygy/block_graph/block_graph.h"
21 : #include "syzygy/core/unittest_util.h"
22 : #include "syzygy/pe/pe_utils.h"
23 : #include "syzygy/reorder/order_generator_test.h"
24 :
25 : #include "mnemonics.h" // NOLINT
26 :
27 : namespace reorder {
28 : namespace {
29 :
30 : using block_graph::BasicBlock;
31 : using block_graph::BasicCodeBlock;
32 : using block_graph::BasicDataBlock;
33 : using block_graph::BlockGraph;
34 : using core::RelativeAddress;
35 : using grinder::basic_block_util::EntryCountType;
36 : using grinder::basic_block_util::IndexedFrequencyInformation;
37 : using grinder::basic_block_util::IndexedFrequencyMap;
38 : using grinder::basic_block_util::LoadBasicBlockRanges;
39 : using grinder::basic_block_util::RelativeAddressRange;
40 : using grinder::basic_block_util::RelativeAddressRangeVector;
41 : using pe::ImageLayout;
42 : using testing::GetExeTestDataRelativePath;
43 :
44 : typedef Reorderer::Order Order;
45 :
46 : class TestBasicBlockOrderer : public BasicBlockOptimizer::BasicBlockOrderer {
47 : public:
48 : using BasicBlockOptimizer::BasicBlockOrderer::GetBasicBlockEntryCount;
49 : using BasicBlockOptimizer::BasicBlockOrderer::GetWarmestSuccessor;
50 : using BasicBlockOptimizer::BasicBlockOrderer::GetSortedJumpTargets;
51 : using BasicBlockOptimizer::BasicBlockOrderer::AddRecursiveDataReferences;
52 : using BasicBlockOptimizer::BasicBlockOrderer::AddWarmDataReferences;
53 :
54 : TestBasicBlockOrderer(
55 : const BasicBlockSubGraph& subgraph,
56 : const RelativeAddress& addr,
57 : Size size,
58 : const IndexedFrequencyInformation& entry_counts)
59 E : : BasicBlockOptimizer::BasicBlockOrderer(
60 E : subgraph, addr, size, entry_counts) {
61 E : }
62 : };
63 :
64 : class BasicBlockOrdererTest : public testing::BasicBlockTest {
65 : public:
66 E : virtual void SetUp() override {
67 E : ASSERT_NO_FATAL_FAILURE(testing::BasicBlockTest::SetUp());
68 E : ASSERT_NO_FATAL_FAILURE(InitBlockGraph());
69 E : ASSERT_NO_FATAL_FAILURE(InitBasicBlockSubGraph());
70 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(0, 0, 0, 0, 0, 0, 0, 0));
71 E : orderer_.reset(new TestBasicBlockOrderer(subgraph_,
72 : start_addr_,
73 : assembly_func_->size(),
74 : entry_counts_));
75 E : ASSERT_TRUE(orderer_.get() != NULL);
76 E : }
77 :
78 : RelativeAddressRange MakeRange(BlockGraph::Offset offset,
79 : BlockGraph::Size size) {
80 : return RelativeAddressRange(start_addr_ + offset, size);
81 : }
82 :
83 E : BasicBlock* FindBasicBlockAt(BlockGraph::Offset offset) {
84 : typedef BasicBlockSubGraph::BBCollection BBCollection;
85 : BasicBlockSubGraph::BBCollection::iterator it =
86 E : subgraph_.basic_blocks().begin();
87 E : for (; it != subgraph_.basic_blocks().end(); ++it) {
88 E : if ((*it)->offset() == offset)
89 E : return *it;
90 E : }
91 i : return NULL;
92 E : }
93 :
94 : void SetEntryCounts(uint32_t bb0,
95 : uint32_t bb1,
96 : uint32_t bb2,
97 : uint32_t bb3,
98 : uint32_t bb4,
99 : uint32_t bb5,
100 : uint32_t bb6,
101 E : uint32_t bb7) {
102 E : entry_counts_.num_entries = kNumCodeBasicBlocks;
103 E : entry_counts_.num_columns = 1;
104 E : entry_counts_.data_type =
105 : ::common::IndexedFrequencyData::BASIC_BLOCK_ENTRY;
106 E : entry_counts_.frequency_size = 4;
107 :
108 E : IndexedFrequencyMap& frequency_map = entry_counts_.frequency_map;
109 E : frequency_map.clear();
110 :
111 E : frequency_map[std::make_pair(start_addr_ + kBasicBlockOffsets[0], 0)] = bb0;
112 E : frequency_map[std::make_pair(start_addr_ + kBasicBlockOffsets[1], 0)] = bb1;
113 E : frequency_map[std::make_pair(start_addr_ + kBasicBlockOffsets[2], 0)] = bb2;
114 E : frequency_map[std::make_pair(start_addr_ + kBasicBlockOffsets[3], 0)] = bb3;
115 E : frequency_map[std::make_pair(start_addr_ + kBasicBlockOffsets[4], 0)] = bb4;
116 E : frequency_map[std::make_pair(start_addr_ + kBasicBlockOffsets[5], 0)] = bb5;
117 E : frequency_map[std::make_pair(start_addr_ + kBasicBlockOffsets[6], 0)] = bb6;
118 E : frequency_map[std::make_pair(start_addr_ + kBasicBlockOffsets[7], 0)] = bb7;
119 E : ASSERT_EQ(kNumCodeBasicBlocks, frequency_map.size());
120 E : }
121 :
122 : static const size_t kBasicBlockOffsets[kNumCodeBasicBlocks];
123 :
124 : IndexedFrequencyInformation entry_counts_;
125 : std::unique_ptr<TestBasicBlockOrderer> orderer_;
126 : };
127 :
128 : const size_t BasicBlockOrdererTest::kBasicBlockOffsets[kNumCodeBasicBlocks] =
129 : { 0, 23, 24, 31, 36, 37, 42, 49 };
130 :
131 : class BasicBlockOptimizerTest : public testing::OrderGeneratorTest {
132 : public:
133 : typedef testing::OrderGeneratorTest Super;
134 :
135 : BasicBlockOptimizerTest()
136 E : : num_decomposable_blocks_(0),
137 E : num_non_decomposable_blocks_(0),
138 E : num_non_code_blocks_(0) {
139 E : }
140 :
141 E : virtual void SetUp() override {
142 E : ASSERT_NO_FATAL_FAILURE(Super::SetUp());
143 E : ASSERT_NO_FATAL_FAILURE(InitBlockCounts());
144 E : base::FilePath pdb_path(GetExeTestDataRelativePath(
145 : testing::kBBEntryInstrumentedTestDllPdbName));
146 E : }
147 :
148 E : void InitBlockCounts() {
149 E : ASSERT_EQ(0U, num_decomposable_blocks_);
150 E : ASSERT_EQ(0U, num_non_decomposable_blocks_);
151 E : ASSERT_EQ(0U, num_non_code_blocks_);
152 :
153 E : for (size_t i = 0; i < image_layout_.sections.size(); ++i) {
154 E : const ImageLayout::SectionInfo& section_info = image_layout_.sections[i];
155 : BlockGraph::AddressSpace::RangeMapConstIterPair ip =
156 E : image_layout_.blocks.GetIntersectingBlocks(section_info.addr,
157 : section_info.size);
158 E : for (; ip.first != ip.second; ++ip.first) {
159 E : const BlockGraph::Block* block = ip.first->second;
160 E : if (block->type() != BlockGraph::CODE_BLOCK) {
161 E : ++num_non_code_blocks_;
162 E : } else if (policy_.BlockIsSafeToBasicBlockDecompose(block)) {
163 E : ++num_decomposable_blocks_;
164 E : } else {
165 E : ++num_non_decomposable_blocks_;
166 : }
167 E : }
168 E : }
169 E : }
170 :
171 : bool FindBlockByName(const base::StringPiece& name,
172 : const BlockGraph::Block** block,
173 E : BlockGraph::AddressSpace::Range* range) {
174 E : DCHECK(block != NULL);
175 E : DCHECK(range != NULL);
176 : BlockGraph::AddressSpace::RangeMapConstIter it =
177 E : image_layout_.blocks.begin();
178 E : for (; it != image_layout_.blocks.end(); ++it) {
179 E : if (it->second->name() == name) {
180 E : *range = it->first;
181 E : *block = it->second;
182 E : return true;
183 : }
184 E : }
185 i : return false;
186 E : }
187 :
188 : protected:
189 : pe::PETransformPolicy policy_;
190 : BasicBlockOptimizer optimizer_;
191 : size_t num_decomposable_blocks_;
192 : size_t num_non_decomposable_blocks_;
193 : size_t num_non_code_blocks_;
194 : };
195 :
196 : } // namespace
197 :
198 E : TEST_F(BasicBlockOrdererTest, GetBlockEntryCount) {
199 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(1, 0, 1, 5, 1, 0, 0, 0));
200 E : EntryCountType entry_count = 0;
201 E : EXPECT_TRUE(orderer_->GetBlockEntryCount(&entry_count));
202 E : EXPECT_EQ(1U, entry_count);
203 :
204 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(17, 0, 1, 5, 1, 0, 0, 0));
205 E : EXPECT_TRUE(orderer_->GetBlockEntryCount(&entry_count));
206 E : EXPECT_EQ(17U, entry_count);
207 E : }
208 :
209 E : TEST_F(BasicBlockOrdererTest, GetWarmestSuccessor) {
210 E : const BasicCodeBlock* sub = BasicCodeBlock::Cast(FindBasicBlockAt(31));
211 E : ASSERT_TRUE(sub != NULL);
212 :
213 E : const BasicCodeBlock* ret = BasicCodeBlock::Cast(FindBasicBlockAt(36));
214 E : ASSERT_TRUE(ret != NULL);
215 :
216 E : TestBasicBlockOrderer::BasicBlockSet placed_bbs;
217 E : const BasicBlock* succ = NULL;
218 :
219 : // Make the fall-through as the warmest successor.
220 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(0, 0, 0, 5, 10, 0, 0, 0));
221 E : ASSERT_TRUE(orderer_->GetWarmestSuccessor(sub, placed_bbs, &succ));
222 E : ASSERT_EQ(ret, succ);
223 :
224 : // Make the branch taken as the warmest successor.
225 E : succ = NULL;
226 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(0, 0, 0, 10, 5, 0, 0, 0));
227 E : ASSERT_TRUE(orderer_->GetWarmestSuccessor(sub, placed_bbs, &succ));
228 E : ASSERT_EQ(sub, succ);
229 :
230 : // Give both branches the same warmth. Should preserve ordering.
231 E : succ = NULL;
232 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(0, 0, 0, 10, 10, 0, 0, 0));
233 E : ASSERT_TRUE(orderer_->GetWarmestSuccessor(sub, placed_bbs, &succ));
234 E : ASSERT_EQ(ret, succ);
235 :
236 : // Let the warmest branch already be placed, should return the other branch.
237 E : succ = NULL;
238 E : placed_bbs.insert(ret);
239 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(0, 0, 0, 5, 10, 0, 0, 0));
240 E : ASSERT_TRUE(orderer_->GetWarmestSuccessor(sub, placed_bbs, &succ));
241 E : ASSERT_EQ(sub, succ);
242 :
243 : // Let the warmest branch already be placed, should return the other branch.
244 : // Note that we initialize succ to non NULL to verify that it becomes NULL.
245 E : succ = sub;
246 E : placed_bbs.insert(sub);
247 E : placed_bbs.insert(ret);
248 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(0, 0, 0, 5, 10, 0, 0, 0));
249 E : ASSERT_TRUE(orderer_->GetWarmestSuccessor(sub, placed_bbs, &succ));
250 E : ASSERT_EQ(NULL, succ);
251 E : }
252 :
253 E : TEST_F(BasicBlockOrdererTest, AddWarmDataReferences) {
254 : // Get basic block pointers to the switch, jump table, and case table.
255 E : const BasicCodeBlock* code_bb = BasicCodeBlock::Cast(FindBasicBlockAt(0));
256 E : const BasicDataBlock* jump_table = BasicDataBlock::Cast(FindBasicBlockAt(52));
257 E : const BasicDataBlock* case_table = BasicDataBlock::Cast(FindBasicBlockAt(64));
258 E : ASSERT_TRUE(code_bb != NULL);
259 E : ASSERT_TRUE(jump_table != NULL);
260 E : ASSERT_TRUE(case_table != NULL);
261 :
262 : // Capture the references from the switch basic block (offset 0).
263 E : TestBasicBlockOrderer::BasicBlockSet references;
264 E : ASSERT_TRUE(orderer_->AddWarmDataReferences(code_bb, &references));
265 E : EXPECT_EQ(2U, references.size());
266 E : EXPECT_EQ(1U, references.count(jump_table));
267 E : EXPECT_EQ(1U, references.count(case_table));
268 :
269 : // Capture the references from the case_0 basic block (offset 24).
270 E : references.clear();
271 E : code_bb = BasicCodeBlock::Cast(FindBasicBlockAt(24));
272 E : ASSERT_TRUE(orderer_->AddWarmDataReferences(code_bb, &references));
273 E : EXPECT_TRUE(references.empty());
274 E : }
275 :
276 E : TEST_F(BasicBlockOrdererTest, GetSortedJumpTargets) {
277 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(0, 0, 2, 0, 0, 1, 3, 0));
278 E : const BasicCodeBlock* first_bb = BasicCodeBlock::Cast(FindBasicBlockAt(0));
279 E : ASSERT_TRUE(first_bb->successors().empty());
280 E : ASSERT_TRUE(!first_bb->instructions().empty());
281 E : const block_graph::Instruction& jmp_inst = first_bb->instructions().back();
282 E : ASSERT_EQ(I_JMP, jmp_inst.representation().opcode);
283 E : logging::SetMinLogLevel(-1);
284 E : std::vector<const BasicCodeBlock*> targets;
285 E : ASSERT_TRUE(orderer_->GetSortedJumpTargets(jmp_inst, &targets));
286 E : ASSERT_THAT(targets,
287 : testing::ElementsAre(
288 : BasicCodeBlock::Cast(FindBasicBlockAt(42)),
289 : BasicCodeBlock::Cast(FindBasicBlockAt(24)),
290 E : BasicCodeBlock::Cast(FindBasicBlockAt(37))));
291 E : }
292 :
293 E : TEST_F(BasicBlockOrdererTest, GetStableSortedJumpTargets) {
294 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(0, 0, 1, 0, 0, 2, 1, 0));
295 E : const BasicCodeBlock* first_bb = BasicCodeBlock::Cast(FindBasicBlockAt(0));
296 E : ASSERT_TRUE(first_bb->successors().empty());
297 E : ASSERT_TRUE(!first_bb->instructions().empty());
298 E : const block_graph::Instruction& jmp_inst = first_bb->instructions().back();
299 E : ASSERT_EQ(I_JMP, jmp_inst.representation().opcode);
300 E : logging::SetMinLogLevel(-1);
301 E : std::vector<const BasicCodeBlock*> targets;
302 E : ASSERT_TRUE(orderer_->GetSortedJumpTargets(jmp_inst, &targets));
303 E : ASSERT_THAT(targets,
304 : testing::ElementsAre(
305 : BasicCodeBlock::Cast(FindBasicBlockAt(37)),
306 : BasicCodeBlock::Cast(FindBasicBlockAt(24)),
307 E : BasicCodeBlock::Cast(FindBasicBlockAt(42))));
308 E : }
309 :
310 E : TEST_F(BasicBlockOrdererTest, HotColdSeparation) {
311 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(1, 0, 1, 5, 1, 0, 0, 0));
312 E : Order::OffsetVector warm;
313 E : Order::OffsetVector cold;
314 E : ASSERT_TRUE(orderer_->GetBasicBlockOrderings(&warm, &cold));
315 : // Note that the bb's at 52 and 64 are the jump and case tables, respectively.
316 E : EXPECT_THAT(warm, testing::ElementsAre(0, 24, 31, 36, 52, 64));
317 E : EXPECT_THAT(cold, testing::ElementsAre(23, 37, 42, 49));
318 E : }
319 :
320 E : TEST_F(BasicBlockOrdererTest, PathStraightening) {
321 : // The default control flow of the block we construct isn't very interesting
322 : // from a path straightening perspective. So, we modify it here such that the
323 : // jnz instruction the end of the basic block at offset 31 branches to case_1
324 : // (at offset 37), and then give that basic-block an elevated entry count.
325 E : BasicCodeBlock* case_1 = BasicCodeBlock::Cast(FindBasicBlockAt(37));
326 E : ASSERT_TRUE(case_1 != NULL);
327 E : ASSERT_EQ(1U, case_1->instructions().size());
328 E : ASSERT_EQ(I_CALL, case_1->instructions().front().representation().opcode);
329 :
330 E : BasicCodeBlock* jnz_bb = BasicCodeBlock::Cast(FindBasicBlockAt(31));
331 E : ASSERT_TRUE(jnz_bb != NULL);
332 E : ASSERT_EQ(1U, jnz_bb->instructions().size());
333 E : ASSERT_EQ(I_SUB, jnz_bb->instructions().front().representation().opcode);
334 E : ASSERT_EQ(2U, jnz_bb->successors().size());
335 E : ASSERT_EQ(block_graph::Successor::kConditionNotEqual,
336 E : jnz_bb->successors().front().condition());
337 E : jnz_bb->successors().front().set_reference(
338 : block_graph::BasicBlockReference(BlockGraph::PC_RELATIVE_REF, 1, case_1));
339 :
340 : // Setup the entry counts such that the jump table stays in the same order
341 : // but case 1 is promoted to follow the jnz basic block.
342 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(1, 0, 10, 5, 1, 7, 0, 0));
343 E : Order::OffsetVector warm;
344 E : Order::OffsetVector cold;
345 E : ASSERT_TRUE(orderer_->GetBasicBlockOrderings(&warm, &cold));
346 : // Note that the bb's at 52 and 64 are the jump and case tables, respectively.
347 E : EXPECT_THAT(warm, testing::ElementsAre(0, 24, 31, 37, 36, 52, 64));
348 E : EXPECT_THAT(cold, testing::ElementsAre(42, 23, 49));
349 E : }
350 :
351 E : TEST_F(BasicBlockOrdererTest, PathStraighteningAcrossJumpTable) {
352 : // Setup the entry counts such that case 1 (at offset 37) is promoted to be
353 : // the warmest path through the jump table.
354 E : ASSERT_NO_FATAL_FAILURE(SetEntryCounts(1, 0, 1, 5, 1, 7, 0, 0));
355 E : Order::OffsetVector warm;
356 E : Order::OffsetVector cold;
357 E : ASSERT_TRUE(orderer_->GetBasicBlockOrderings(&warm, &cold));
358 : // Note that the bb's at 52 and 64 are the jump and case tables, respectively.
359 E : EXPECT_THAT(warm, testing::ElementsAre(0, 37, 24, 31, 36, 52, 64));
360 E : EXPECT_THAT(cold, testing::ElementsAre(42, 23, 49));
361 E : }
362 :
363 E : TEST_F(BasicBlockOptimizerTest, Accessors) {
364 E : const std::string kSectionName(".froboz");
365 E : EXPECT_TRUE(!optimizer_.cold_section_name().empty());
366 E : EXPECT_NE(kSectionName, optimizer_.cold_section_name());
367 E : optimizer_.set_cold_section_name(kSectionName);
368 E : EXPECT_EQ(kSectionName, optimizer_.cold_section_name());
369 E : }
370 :
371 E : TEST_F(BasicBlockOptimizerTest, EmptyOrderingAllCold) {
372 E : Order order;
373 E : IndexedFrequencyInformation entry_counts;
374 E : entry_counts.num_entries = 0;
375 E : entry_counts.num_columns = 1;
376 E : entry_counts.data_type = ::common::IndexedFrequencyData::BASIC_BLOCK_ENTRY;
377 E : entry_counts.frequency_size = 4;
378 :
379 E : ASSERT_TRUE(
380 E : optimizer_.Optimize(image_layout_, entry_counts, &order));
381 :
382 E : EXPECT_EQ(image_layout_.sections.size() + 1, order.sections.size());
383 E : EXPECT_EQ(optimizer_.cold_section_name(), order.sections.back().name);
384 E : EXPECT_EQ(Order::SectionSpec::kNewSectionId, order.sections.back().id);
385 E : EXPECT_EQ(pe::kCodeCharacteristics, order.sections.back().characteristics);
386 :
387 : // Count the blocks left in the original sections. This should only include
388 : // non-code blocks.
389 E : size_t num_non_code_blocks = 0;
390 E : for (size_t i = 0; i < image_layout_.sections.size(); ++i) {
391 E : for (size_t k = 0; k < order.sections[i].blocks.size(); ++k) {
392 E : const BlockGraph::Block* block = order.sections[i].blocks[k].block;
393 E : ASSERT_TRUE(block != NULL);
394 E : ASSERT_NE(BlockGraph::CODE_BLOCK, block->type());
395 E : ++num_non_code_blocks;
396 E : }
397 E : }
398 :
399 : // Validate that we have the expected numbers of blocks.
400 E : EXPECT_EQ(num_non_code_blocks_, num_non_code_blocks);
401 E : EXPECT_EQ(num_decomposable_blocks_ + num_non_decomposable_blocks_,
402 E : order.sections.back().blocks.size());
403 E : for (size_t i = 0; i < order.sections.back().blocks.size(); ++i) {
404 E : EXPECT_TRUE(order.sections.back().blocks[i].basic_block_offsets.empty());
405 E : }
406 E : }
407 :
408 E : TEST_F(BasicBlockOptimizerTest, HotCold) {
409 : // This test does a simple manipulation of the entry counts for DllMain and
410 : // validates that some minimum number of its blocks get moved into the cold
411 : // section. We defer to the BasicBlockOrdererTest instances above for the
412 : // details Hot/Cold and Path Straightening tests.
413 E : const BlockGraph::Block* dllmain = NULL;
414 E : BlockGraph::AddressSpace::Range range;
415 E : ASSERT_TRUE(FindBlockByName("DllMain", &dllmain, &range));
416 E : ASSERT_TRUE(dllmain != NULL);
417 :
418 : using block_graph::BasicBlockSubGraph;
419 : using block_graph::BasicBlockDecomposer;
420 :
421 E : BasicBlockSubGraph subgraph;
422 E : BasicBlockDecomposer decomposer(dllmain, &subgraph);
423 E : ASSERT_TRUE(decomposer.Decompose());
424 E : ASSERT_EQ(1U, subgraph.block_descriptions().size());
425 :
426 : // Generate an entry count map with a non-zero count for every other BB.
427 E : IndexedFrequencyInformation entry_counts;
428 E : entry_counts.num_entries = 0;
429 E : entry_counts.num_columns = 1;
430 E : entry_counts.data_type = ::common::IndexedFrequencyData::BASIC_BLOCK_ENTRY;
431 E : entry_counts.frequency_size = 4;
432 E : IndexedFrequencyMap& frequency_map = entry_counts.frequency_map;
433 :
434 E : const BasicBlockSubGraph::BlockDescription& desc =
435 : subgraph.block_descriptions().front();
436 : BasicBlockSubGraph::BasicBlockOrdering::const_iterator it(
437 E : desc.basic_block_order.begin());
438 E : size_t num_basic_blocks = desc.basic_block_order.size();
439 E : size_t num_hot_blocks = 0;
440 :
441 E : bool is_hot = true;
442 E : BlockGraph::RelativeAddress start_offs = subgraph.original_block()->addr();
443 E : for (; it != desc.basic_block_order.end(); ++it) {
444 E : if (is_hot && BasicCodeBlock::Cast(*it) != NULL) {
445 E : frequency_map[std::make_pair(start_offs + (*it)->offset(), 0)] = 1;
446 E : ++num_hot_blocks;
447 : }
448 :
449 : // Toggle hotness for next block.
450 E : is_hot = !is_hot;
451 E : }
452 :
453 : // Create an ordering that moves dllmain to a new section.
454 E : std::string section_name(".dllmain");
455 E : Order order;
456 E : order.sections.resize(1);
457 E : order.sections[0].id = Order::SectionSpec::kNewSectionId;
458 E : order.sections[0].name = section_name;
459 E : order.sections[0].characteristics = pe::kCodeCharacteristics;
460 E : order.sections[0].blocks.push_back(Order::BlockSpec(dllmain));
461 :
462 E : ASSERT_TRUE(
463 E : optimizer_.Optimize(image_layout_, entry_counts, &order));
464 :
465 E : ASSERT_EQ(image_layout_.sections.size() + 2, order.sections.size());
466 E : ASSERT_EQ(section_name, order.sections[0].name);
467 E : ASSERT_EQ(1U, order.sections[0].blocks.size());
468 E : ASSERT_TRUE(!order.sections.back().blocks.empty());
469 E : ASSERT_EQ(dllmain, order.sections[0].blocks[0].block);
470 E : ASSERT_EQ(dllmain, order.sections.back().blocks[0].block);
471 E : ASSERT_LE(num_hot_blocks,
472 E : order.sections[0].blocks[0].basic_block_offsets.size());
473 :
474 : // Since data BBs that are referred to by 'hot' code BBs also make
475 : // it into the hot BB list, there could be fewer cold blocks than expected.
476 E : ASSERT_GE(num_basic_blocks - num_hot_blocks,
477 E : order.sections.back().blocks[0].basic_block_offsets.size());
478 E : }
479 :
480 : } // namespace reorder
|