Coverage for /Syzygy/optimize/transforms/inlining_transform_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
99.6%2772780.C++test

Line-by-line coverage:

   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

Coverage information generated Thu Jan 14 17:40:38 2016.