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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
99.1%3183210.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_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

Coverage information generated Fri Jul 29 11:00:21 2016.