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