1 : // Copyright 2013 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/optimize/transforms/inlining_transform.h"
16 :
17 : #include "gmock/gmock.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/block_graph/basic_block.h"
20 : #include "syzygy/block_graph/basic_block_assembler.h"
21 : #include "syzygy/block_graph/basic_block_decomposer.h"
22 : #include "syzygy/block_graph/basic_block_subgraph.h"
23 : #include "syzygy/block_graph/block_builder.h"
24 : #include "syzygy/block_graph/block_graph.h"
25 : #include "syzygy/block_graph/unittest_util.h"
26 : #include "syzygy/optimize/application_profile.h"
27 : #include "syzygy/pe/pe_transform_policy.h"
28 :
29 : namespace optimize {
30 : namespace transforms {
31 : namespace {
32 :
33 : using block_graph::BasicBlock;
34 : using block_graph::BasicBlockAssembler;
35 : using block_graph::BasicBlockDecomposer;
36 : using block_graph::BasicBlockReference;
37 : using block_graph::BasicBlockSubGraph;
38 : using block_graph::BlockBuilder;
39 : using block_graph::BlockGraph;
40 : using block_graph::Displacement;
41 : using block_graph::Immediate;
42 : using block_graph::Instruction;
43 : using block_graph::Operand;
44 : using block_graph::Successor;
45 : using pe::ImageLayout;
46 : using testing::ElementsAreArray;
47 :
48 : typedef BasicBlockSubGraph::BasicCodeBlock BasicCodeBlock;
49 : typedef BlockGraph::Offset Offset;
50 :
51 : // This enum is used to drive the contents of the callee.
52 : enum CalleeKind {
53 : // Block DirectTrampoline
54 : // dummy: jmp target
55 : kDirectTrampoline,
56 : // Block DirectTrampoline
57 : // dummy: push eax
58 : // jmp target
59 : kDirectTrampolineWithInstruction,
60 : // Block IndirectTrampoline
61 : // dummy: jmp [target]
62 : kIndirectTrampoline,
63 : // Block RecursiveTrampoline
64 : // dummy: jmp dummy
65 : kRecursiveTrampoline,
66 : };
67 :
68 : const uint8_t kData[] = {0x01, 0x02, 0x03, 0x04};
69 :
70 : // _asm ret
71 : const uint8_t kCodeRet[] = {0xC3};
72 :
73 : // _asm push ebp
74 : // _asm mov ebp, esp
75 : // _asm pop ebp
76 : // _asm ret
77 : const uint8_t kCodeEmpty[] = {0x55, 0x8B, 0xEC, 0x5D, 0xC3};
78 :
79 : // _asm push ebp
80 : // _asm mov ebp, esp
81 : // _asm pop ebp
82 : // _asm xor eax, eax
83 : // _asm ret
84 : const uint8_t kCodeOptimizeRet0[] = {0x55, 0x8B, 0xEC, 0x5D, 0x33, 0xC0, 0xC3};
85 :
86 : // _asm ret8
87 : const uint8_t kCodeRetWithOffset[] = {0xC2, 0x08, 0x00};
88 :
89 : // _asm lea esp, [esp + 8]
90 : const uint8_t kCodeLeaEsp8[] = {0x8D, 0x64, 0x24, 0x08, 0xC3};
91 :
92 : // _asm xor eax, eax
93 : // _asm ret
94 : const uint8_t kCodeRet0[] = {0x33, 0xC0, 0xC3};
95 :
96 : // _asm xor eax, eax
97 : const uint8_t kCodeMov0[] = {0x33, 0xC0};
98 :
99 : // _asm mov eax, 2Ah
100 : // _asm ret
101 : const uint8_t kCodeRet42[] = {0xB8, 0x2A, 0x00, 0x00, 0x00, 0xC3};
102 :
103 : // _asm xor eax, eax
104 : // _asm mov eax, 2Ah
105 : // _asm ret
106 : const uint8_t kCodeRetBoth[] = {0x33, 0xC0, 0xB8, 0x2A, 0x00, 0x00, 0x00, 0xC3};
107 :
108 : // _asm mov eax, esp
109 : // _asm ret
110 : const uint8_t kCodeMovStack[] = {0x8B, 0xC4, 0xC3};
111 :
112 : // _asm ret (16x)
113 : const uint8_t kCodeBig[] = {0xC3,
114 : 0xC3,
115 : 0xC3,
116 : 0xC3,
117 : 0xC3,
118 : 0xC3,
119 : 0xC3,
120 : 0xC3,
121 : 0xC3,
122 : 0xC3,
123 : 0xC3,
124 : 0xC3,
125 : 0xC3,
126 : 0xC3,
127 : 0xC3,
128 : 0xC3};
129 :
130 : // _asm je here
131 : // _asm xor eax, eax
132 : // here:
133 : // _asm ret
134 : const uint8_t kCodeJump[] = {0x74, 0x02, 0x33, 0xC0, 0xC3};
135 :
136 : // here:
137 : // _asm or eax,eax
138 : // _asm jne here
139 : // _asm ret
140 : const uint8_t kCodeSelfJump[] = {0x0B, 0xC0, 0x75, 0xFC, 0xC3};
141 :
142 : // _asm call dword ptr [eax]
143 : // _asm ret
144 : const uint8_t kCodeIndirectCall[] = {0xFF, 0x55, 0xF8, 0xC3};
145 :
146 : // _asm push 2
147 : // _asm pop eax
148 : // _asm ret
149 : const uint8_t kStackCst[] = {0x6A, 0x02, 0x58, 0xC3};
150 :
151 : class TestInliningTransform : public InliningTransform {
152 : public:
153 : using InliningTransform::subgraph_cache_;
154 : };
155 :
156 : class InliningTransformTest : public testing::Test {
157 : public:
158 : InliningTransformTest()
159 E : : data_(NULL),
160 E : caller_(NULL),
161 E : callee_(NULL),
162 E : image_(&block_graph_),
163 E : profile_(&image_) {
164 E : }
165 :
166 E : virtual void SetUp() {
167 E : caller_ =
168 : block_graph_.AddBlock(BlockGraph::CODE_BLOCK, sizeof(kCodeRet), "ret");
169 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), caller_);
170 E : caller_->SetData(kCodeRet, sizeof(kCodeRet));
171 E : caller_->SetLabel(0, "code", BlockGraph::CODE_LABEL);
172 :
173 E : data_ = block_graph_.AddBlock(BlockGraph::DATA_BLOCK, sizeof(kData), "int");
174 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), data_);
175 E : data_->SetData(kData, sizeof(kData));
176 E : }
177 :
178 : protected:
179 : void AddBlockFromBuffer(const uint8_t* data,
180 : size_t length,
181 : BlockGraph::Block** block);
182 : void CreateCalleeBlock(CalleeKind kind,
183 : BlockGraph::Block* target,
184 : BlockGraph::Block** callee);
185 : void CreateCallSiteToBlock(BlockGraph::Block* callee);
186 : void ApplyTransformOnCaller();
187 : void SaveCaller();
188 :
189 : pe::PETransformPolicy policy_;
190 : BlockGraph block_graph_;
191 : BlockGraph::Block* data_;
192 : BlockGraph::Block* caller_;
193 : BlockGraph::Block* callee_;
194 : std::vector<uint8_t> original_;
195 : BasicBlockSubGraph callee_subgraph_;
196 : ImageLayout image_;
197 : ApplicationProfile profile_;
198 : SubGraphProfile subgraph_profile_;
199 : };
200 :
201 : void InliningTransformTest::AddBlockFromBuffer(const uint8_t* data,
202 : size_t length,
203 E : BlockGraph::Block** block) {
204 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block**>(NULL), block);
205 E : *block = block_graph_.AddBlock(BlockGraph::CODE_BLOCK, length, "test");
206 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), *block);
207 E : (*block)->SetData(data, length);
208 E : (*block)->SetLabel(0, "code", BlockGraph::CODE_LABEL);
209 E : }
210 :
211 : // Produce a callee block. The content of the body is determined by |kind|.
212 : void InliningTransformTest::CreateCalleeBlock(CalleeKind kind,
213 : BlockGraph::Block* target,
214 E : BlockGraph::Block** callee) {
215 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block**>(NULL), callee);
216 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), *callee);
217 :
218 : // If the block is labeled, preserve the label.
219 : // TODO(etienneb): Get rid of this when we fix label/symbol handling properly.
220 E : BlockGraph::Label label;
221 E : (*callee)->GetLabel(Offset(0), &label);
222 :
223 : // Decompose to subgraph.
224 E : BasicBlockSubGraph subgraph;
225 E : BasicBlockDecomposer decomposer(*callee, &subgraph);
226 E : ASSERT_TRUE(decomposer.Decompose());
227 :
228 : // Retrieve the single basic code block.
229 E : ASSERT_EQ(2U, subgraph.basic_blocks().size());
230 E : BasicCodeBlock* code = BasicCodeBlock::Cast(*subgraph.basic_blocks().begin());
231 E : DCHECK_NE(reinterpret_cast<BasicCodeBlock*>(NULL), code);
232 :
233 : // Clear instructions and open an assembler at the start of the basic block.
234 E : BasicBlock::Instructions& instructions = code->instructions();
235 E : instructions.clear();
236 E : BasicBlockAssembler assembler(instructions.begin(), &instructions);
237 :
238 E : switch (kind) {
239 : case kRecursiveTrampoline:
240 E : assembler.jmp(Immediate(code));
241 E : break;
242 : case kDirectTrampoline: {
243 E : Successor successor(
244 : Successor::kConditionTrue,
245 : BasicBlockReference(BlockGraph::PC_RELATIVE_REF, 4, target, 0, 0),
246 : 4);
247 E : code->successors().push_back(successor);
248 E : break;
249 i : }
250 : case kDirectTrampolineWithInstruction: {
251 E : assembler.push(assm::eax);
252 E : Successor successor(
253 : Successor::kConditionTrue,
254 : BasicBlockReference(BlockGraph::PC_RELATIVE_REF, 4, target, 0, 0),
255 : 4);
256 E : code->successors().push_back(successor);
257 E : break;
258 i : }
259 : case kIndirectTrampoline:
260 E : assembler.jmp(Operand(Displacement(target, 0, 0)));
261 E : break;
262 : default:
263 i : NOTREACHED() << "Invalid callee kind.";
264 : }
265 :
266 : // Rebuild block.
267 E : BlockBuilder builder(&block_graph_);
268 E : ASSERT_TRUE(builder.Merge(&subgraph));
269 E : CHECK_EQ(1u, builder.new_blocks().size());
270 E : *callee = *builder.new_blocks().begin();
271 :
272 : // Restore the label.
273 E : if (label.IsValid())
274 E : (*callee)->SetLabel(Offset(0), label);
275 E : };
276 :
277 E : void InliningTransformTest::CreateCallSiteToBlock(BlockGraph::Block* callee) {
278 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), caller_);
279 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), callee);
280 :
281 : // Decompose to subgraph.
282 E : BasicBlockSubGraph subgraph;
283 E : BasicBlockDecomposer decomposer(caller_, &subgraph);
284 E : ASSERT_TRUE(decomposer.Decompose());
285 :
286 : // Retrieve the single basic code block.
287 E : ASSERT_EQ(2U, subgraph.basic_blocks().size());
288 E : BasicCodeBlock* code = BasicCodeBlock::Cast(*subgraph.basic_blocks().begin());
289 E : DCHECK_NE(reinterpret_cast<BasicCodeBlock*>(NULL), code);
290 :
291 : // Encode a call at the entry of this basic block.
292 E : BasicBlock::Instructions& instructions = code->instructions();
293 E : BasicBlockAssembler assembler(instructions.begin(), &instructions);
294 E : assembler.call(Immediate(callee, 0, 0));
295 :
296 : // Rebuild block.
297 E : BlockBuilder builder(&block_graph_);
298 E : ASSERT_TRUE(builder.Merge(&subgraph));
299 E : CHECK_EQ(1u, builder.new_blocks().size());
300 E : caller_ = *builder.new_blocks().begin();
301 :
302 : // Keep track of the original raw bytes from the caller block.
303 E : SaveCaller();
304 E : };
305 :
306 E : void InliningTransformTest::SaveCaller() {
307 : // Keep track of the original raw bytes from the caller block.
308 E : ASSERT_LT(0U, caller_->size());
309 E : original_.resize(caller_->size());
310 E : ::memcpy(&original_[0], caller_->data(), caller_->size());
311 E : }
312 :
313 E : void InliningTransformTest::ApplyTransformOnCaller() {
314 : // Decompose to subgraph.
315 E : BasicBlockSubGraph subgraph;
316 E : BasicBlockDecomposer decomposer(caller_, &subgraph);
317 E : ASSERT_TRUE(decomposer.Decompose());
318 :
319 : // Apply inlining transform.
320 E : InliningTransform tx;
321 E : ASSERT_TRUE(
322 : tx.TransformBasicBlockSubGraph(&policy_, &block_graph_, &subgraph,
323 E : &profile_, &subgraph_profile_));
324 :
325 : // Rebuild block.
326 E : BlockBuilder builder(&block_graph_);
327 E : ASSERT_TRUE(builder.Merge(&subgraph));
328 E : CHECK_EQ(1u, builder.new_blocks().size());
329 E : caller_ = *builder.new_blocks().begin();
330 E : }
331 :
332 : } // namespace
333 :
334 E : TEST_F(InliningTransformTest, SubgraphCache) {
335 E : TestInliningTransform tx;
336 :
337 : // Create a valid inlining candidate.
338 E : ASSERT_NO_FATAL_FAILURE(
339 E : AddBlockFromBuffer(kCodeRet42, sizeof(kCodeRet42), &callee_));
340 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
341 :
342 : // The cache must be empty.
343 E : EXPECT_TRUE(tx.subgraph_cache_.empty());
344 :
345 : // Decompose to subgraph.
346 E : BasicBlockSubGraph subgraph;
347 E : BasicBlockDecomposer decomposer(caller_, &subgraph);
348 E : ASSERT_TRUE(decomposer.Decompose());
349 :
350 : // Apply inlining transform.
351 E : ASSERT_TRUE(
352 : tx.TransformBasicBlockSubGraph(&policy_, &block_graph_, &subgraph,
353 E : &profile_, &subgraph_profile_));
354 :
355 : // Expect the subgraph to be cached.
356 E : EXPECT_EQ(1U, tx.subgraph_cache_.size());
357 E : }
358 :
359 E : TEST_F(InliningTransformTest, PreTransformValidation) {
360 E : ASSERT_NO_FATAL_FAILURE(
361 E : AddBlockFromBuffer(kCodeRet, sizeof(kCodeRet), &callee_));
362 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
363 :
364 : // Caller and callee aren't modified without applying the transform.
365 E : EXPECT_EQ(6U, caller_->size());
366 E : EXPECT_EQ(1U, callee_->size());
367 E : EXPECT_THAT(kCodeRet, ElementsAreArray(callee_->data(), callee_->size()));
368 E : }
369 :
370 E : TEST_F(InliningTransformTest, InlineTrivialRet) {
371 E : ASSERT_NO_FATAL_FAILURE(
372 E : AddBlockFromBuffer(kCodeRet, sizeof(kCodeRet), &callee_));
373 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
374 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
375 :
376 : // Expect inlining expansion on caller.
377 E : EXPECT_THAT(kCodeRet, ElementsAreArray(caller_->data(), caller_->size()));
378 E : EXPECT_THAT(kCodeRet, ElementsAreArray(callee_->data(), callee_->size()));
379 E : }
380 :
381 E : TEST_F(InliningTransformTest, InlineTrivialRet0) {
382 E : ASSERT_NO_FATAL_FAILURE(
383 E : AddBlockFromBuffer(kCodeRet0, sizeof(kCodeRet0), &callee_));
384 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
385 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
386 :
387 E : EXPECT_THAT(kCodeRet0, ElementsAreArray(caller_->data(), caller_->size()));
388 E : EXPECT_THAT(kCodeRet0, ElementsAreArray(callee_->data(), callee_->size()));
389 E : }
390 :
391 E : TEST_F(InliningTransformTest, InlineTrivialRet42) {
392 E : ASSERT_NO_FATAL_FAILURE(
393 E : AddBlockFromBuffer(kCodeRet42, sizeof(kCodeRet42), &callee_));
394 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
395 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
396 :
397 E : EXPECT_THAT(kCodeRet42, ElementsAreArray(caller_->data(), caller_->size()));
398 E : EXPECT_THAT(kCodeRet42, ElementsAreArray(callee_->data(), callee_->size()));
399 E : }
400 :
401 E : TEST_F(InliningTransformTest, InlineEmptyBody) {
402 E : ASSERT_NO_FATAL_FAILURE(
403 E : AddBlockFromBuffer(kCodeEmpty, sizeof(kCodeEmpty), &callee_));
404 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
405 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
406 :
407 E : EXPECT_THAT(kCodeRet, ElementsAreArray(caller_->data(), caller_->size()));
408 E : }
409 :
410 E : TEST_F(InliningTransformTest, InlineTrivialTwoCalls) {
411 E : BlockGraph::Block* callee1 = NULL;
412 E : BlockGraph::Block* callee2 = NULL;
413 :
414 E : ASSERT_NO_FATAL_FAILURE(
415 E : AddBlockFromBuffer(kCodeRet0, sizeof(kCodeRet0), &callee1));
416 E : ASSERT_NO_FATAL_FAILURE(
417 E : AddBlockFromBuffer(kCodeRet42, sizeof(kCodeRet42), &callee2));
418 :
419 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee2));
420 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee1));
421 :
422 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
423 :
424 : // Expect both calls to be inlined and instructions to be in the right order.
425 E : EXPECT_THAT(kCodeRetBoth,
426 E : ElementsAreArray(caller_->data(), caller_->size()));
427 E : }
428 :
429 E : TEST_F(InliningTransformTest, InlineReturnWithOffset) {
430 E : ASSERT_NO_FATAL_FAILURE(
431 : AddBlockFromBuffer(kCodeRetWithOffset,
432 : sizeof(kCodeRetWithOffset),
433 E : &callee_));
434 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
435 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
436 :
437 : // A return with an offset is inlined.
438 E : EXPECT_THAT(kCodeLeaEsp8, ElementsAreArray(caller_->data(), caller_->size()));
439 E : }
440 :
441 E : TEST_F(InliningTransformTest, DontInlineStackManipulation) {
442 E : ASSERT_NO_FATAL_FAILURE(
443 E : AddBlockFromBuffer(kCodeMovStack, sizeof(kCodeMovStack), &callee_));
444 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
445 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
446 :
447 : // Taking address of the stack cannot be inlined. (i.e. stack manipulation).
448 E : EXPECT_THAT(original_, ElementsAreArray(caller_->data(), caller_->size()));
449 E : }
450 :
451 E : TEST_F(InliningTransformTest, DontInlineIndirectCall) {
452 E : ASSERT_NO_FATAL_FAILURE(
453 : AddBlockFromBuffer(kCodeRetWithOffset,
454 : sizeof(kCodeRetWithOffset),
455 E : &callee_));
456 E : ASSERT_NO_FATAL_FAILURE(
457 : AddBlockFromBuffer(kCodeIndirectCall,
458 : sizeof(kCodeIndirectCall),
459 E : &caller_));
460 E : ASSERT_NO_FATAL_FAILURE(SaveCaller());
461 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
462 :
463 : // Taking address of the stack cannot be inlined. (i.e. stack manipulation).
464 E : EXPECT_THAT(original_, ElementsAreArray(caller_->data(), caller_->size()));
465 E : }
466 :
467 E : TEST_F(InliningTransformTest, DontInlineNoReturn) {
468 E : ASSERT_NO_FATAL_FAILURE(
469 E : AddBlockFromBuffer(kCodeMov0, sizeof(kCodeMov0), &callee_));
470 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
471 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
472 :
473 : // No returns found, could not inlined.
474 E : EXPECT_THAT(original_, ElementsAreArray(caller_->data(), caller_->size()));
475 E : }
476 :
477 E : TEST_F(InliningTransformTest, DontInlineData) {
478 : BlockGraph::Block* data_ =
479 E : block_graph_.AddBlock(BlockGraph::DATA_BLOCK, sizeof(kCodeRet0), "d1");
480 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), data_);
481 E : data_->SetData(kCodeRet0, sizeof(kCodeRet0));
482 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(data_));
483 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
484 :
485 : // Data block cannot be inlined.
486 E : EXPECT_THAT(original_, ElementsAreArray(caller_->data(), caller_->size()));
487 E : }
488 :
489 E : TEST_F(InliningTransformTest, DontInlineForwardControlFlow) {
490 E : ASSERT_NO_FATAL_FAILURE(
491 E : AddBlockFromBuffer(kCodeJump, sizeof(kCodeJump), &callee_));
492 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
493 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
494 :
495 E : EXPECT_THAT(original_, ElementsAreArray(caller_->data(), caller_->size()));
496 E : }
497 :
498 E : TEST_F(InliningTransformTest, DontInlineSelfControlFlow) {
499 E : ASSERT_NO_FATAL_FAILURE(
500 E : AddBlockFromBuffer(kCodeSelfJump, sizeof(kCodeSelfJump), &callee_));
501 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
502 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
503 :
504 E : EXPECT_THAT(original_, ElementsAreArray(caller_->data(), caller_->size()));
505 E : }
506 :
507 E : TEST_F(InliningTransformTest, DontInlineBigBlock) {
508 E : ASSERT_NO_FATAL_FAILURE(
509 E : AddBlockFromBuffer(kCodeBig, sizeof(kCodeBig), &callee_));
510 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
511 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
512 :
513 : // Big block cannot be inlined.
514 E : EXPECT_THAT(original_, ElementsAreArray(caller_->data(), caller_->size()));
515 E : }
516 :
517 E : TEST_F(InliningTransformTest, DontInlineCallerPolicy) {
518 E : ASSERT_NO_FATAL_FAILURE(
519 E : AddBlockFromBuffer(kCodeRet0, sizeof(kCodeRet0), &callee_));
520 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
521 :
522 E : caller_->set_attributes(BlockGraph::HAS_EXCEPTION_HANDLING);
523 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
524 :
525 : // Cannot inline exception handling. (i.e. policy handling).
526 E : EXPECT_THAT(original_, ElementsAreArray(caller_->data(), caller_->size()));
527 E : }
528 :
529 E : TEST_F(InliningTransformTest, DontInlineCalleePolicy) {
530 E : ASSERT_NO_FATAL_FAILURE(
531 E : AddBlockFromBuffer(kCodeRet0, sizeof(kCodeRet0), &callee_));
532 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
533 :
534 E : callee_->set_attributes(BlockGraph::HAS_EXCEPTION_HANDLING);
535 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
536 :
537 : // Cannot inline exception handling. (i.e. policy handling).
538 E : EXPECT_THAT(original_, ElementsAreArray(caller_->data(), caller_->size()));
539 E : }
540 :
541 E : TEST_F(InliningTransformTest, DontInfiniteLoopOnSelfTrampoline) {
542 E : ASSERT_NO_FATAL_FAILURE(
543 E : AddBlockFromBuffer(kCodeRet, sizeof(kCodeRet), &callee_));
544 E : ASSERT_NO_FATAL_FAILURE(
545 E : CreateCalleeBlock(kRecursiveTrampoline, callee_, &callee_));
546 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
547 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
548 E : }
549 :
550 E : TEST_F(InliningTransformTest, InlineTrampolineToCode) {
551 E : BlockGraph::Block* dummy = NULL;
552 E : ASSERT_NO_FATAL_FAILURE(
553 E : AddBlockFromBuffer(kCodeRet42, sizeof(kCodeRet42), &dummy));
554 E : ASSERT_NO_FATAL_FAILURE(
555 E : AddBlockFromBuffer(kCodeRet, sizeof(kCodeRet), &callee_));
556 E : ASSERT_NO_FATAL_FAILURE(
557 E : CreateCalleeBlock(kDirectTrampoline, dummy, &callee_));
558 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
559 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
560 :
561 : // Validate that the reference from caller is to dummy.
562 E : ASSERT_EQ(1U, caller_->references().size());
563 E : BlockGraph::Reference reference = caller_->references().begin()->second;
564 E : EXPECT_EQ(dummy, reference.referenced());
565 E : }
566 :
567 E : TEST_F(InliningTransformTest, InlineTrampolineWithInstruction) {
568 E : BlockGraph::Block* dummy = NULL;
569 E : ASSERT_NO_FATAL_FAILURE(
570 E : AddBlockFromBuffer(kCodeRet42, sizeof(kCodeRet42), &dummy));
571 E : ASSERT_NO_FATAL_FAILURE(
572 E : AddBlockFromBuffer(kCodeRet, sizeof(kCodeRet), &callee_));
573 E : ASSERT_NO_FATAL_FAILURE(
574 E : CreateCalleeBlock(kDirectTrampolineWithInstruction, dummy, &callee_));
575 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
576 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
577 :
578 : // Validate that the reference from caller is to dummy.
579 E : ASSERT_EQ(1U, caller_->references().size());
580 E : BlockGraph::Reference reference = caller_->references().begin()->second;
581 E : EXPECT_EQ(dummy, reference.referenced());
582 :
583 : // The first instruction must be a push %eax.
584 E : uint8_t kPushEaxOpcode = 0x50;
585 E : EXPECT_EQ(kPushEaxOpcode, caller_->data()[0]);
586 E : }
587 :
588 E : TEST_F(InliningTransformTest, InlineTrampolineToData) {
589 : BlockGraph::Block* dummy =
590 E : block_graph_.AddBlock(BlockGraph::DATA_BLOCK, sizeof(kCodeRet0), "d1");
591 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), dummy);
592 E : dummy->SetData(kCodeRet0, sizeof(kCodeRet0));
593 E : ASSERT_NO_FATAL_FAILURE(
594 E : AddBlockFromBuffer(kCodeRet, sizeof(kCodeRet), &callee_));
595 E : ASSERT_NO_FATAL_FAILURE(
596 E : CreateCalleeBlock(kDirectTrampoline, dummy, &callee_));
597 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
598 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
599 :
600 : // Validate that the reference from caller is to dummy.
601 E : ASSERT_EQ(1U, caller_->references().size());
602 E : BlockGraph::Reference reference = caller_->references().begin()->second;
603 E : EXPECT_EQ(dummy, reference.referenced());
604 E : }
605 :
606 E : TEST_F(InliningTransformTest, InlineIndirectTrampoline) {
607 E : ASSERT_NO_FATAL_FAILURE(
608 E : AddBlockFromBuffer(kCodeRet, sizeof(kCodeRet), &callee_));
609 E : ASSERT_NO_FATAL_FAILURE(
610 E : CreateCalleeBlock(kIndirectTrampoline, data_, &callee_));
611 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
612 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
613 :
614 : // Validate that the reference from caller is to |data_|.
615 E : ASSERT_EQ(1U, caller_->references().size());
616 E : BlockGraph::Reference reference = caller_->references().begin()->second;
617 E : EXPECT_EQ(data_, reference.referenced());
618 E : }
619 :
620 E : TEST_F(InliningTransformTest, InlineConstantOnStack) {
621 E : ASSERT_NO_FATAL_FAILURE(
622 E : AddBlockFromBuffer(kStackCst, sizeof(kStackCst), &callee_));
623 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
624 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
625 :
626 E : EXPECT_THAT(kStackCst, ElementsAreArray(caller_->data(), caller_->size()));
627 E : }
628 :
629 E : TEST_F(InliningTransformTest, InlineOptimizedRet0) {
630 E : ASSERT_NO_FATAL_FAILURE(
631 : AddBlockFromBuffer(kCodeOptimizeRet0, sizeof(kCodeOptimizeRet0),
632 E : &callee_));
633 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
634 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
635 :
636 E : EXPECT_THAT(kCodeRet0, ElementsAreArray(caller_->data(), caller_->size()));
637 E : }
638 :
639 E : TEST_F(InliningTransformTest, InlineTrampolineToCaller) {
640 E : ASSERT_NO_FATAL_FAILURE(
641 E : AddBlockFromBuffer(kCodeRet, sizeof(kCodeRet), &callee_));
642 E : ASSERT_NO_FATAL_FAILURE(
643 E : CreateCalleeBlock(kDirectTrampoline, caller_, &callee_));
644 E : ASSERT_NO_FATAL_FAILURE(CreateCallSiteToBlock(callee_));
645 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformOnCaller());
646 :
647 : // Validate that the reference from caller is valid, and branch to caller.
648 E : ASSERT_EQ(1U, caller_->references().size());
649 E : BlockGraph::Reference reference = caller_->references().begin()->second;
650 E : EXPECT_EQ(caller_, reference.referenced());
651 E : }
652 :
653 : } // namespace transforms
654 : } // namespace optimize
|