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