Home | History | Annotate | Download | only in compiler
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/compiler/effect-control-linearizer.h"
      6 #include "src/compiler/access-builder.h"
      7 #include "src/compiler/js-graph.h"
      8 #include "src/compiler/linkage.h"
      9 #include "src/compiler/node-properties.h"
     10 #include "src/compiler/schedule.h"
     11 #include "src/compiler/simplified-operator.h"
     12 #include "test/unittests/compiler/graph-unittest.h"
     13 #include "test/unittests/compiler/node-test-utils.h"
     14 #include "test/unittests/test-utils.h"
     15 #include "testing/gmock/include/gmock/gmock.h"
     16 
     17 namespace v8 {
     18 namespace internal {
     19 namespace compiler {
     20 
     21 class EffectControlLinearizerTest : public TypedGraphTest {
     22  public:
     23   EffectControlLinearizerTest()
     24       : TypedGraphTest(3),
     25         machine_(zone()),
     26         javascript_(zone()),
     27         simplified_(zone()),
     28         jsgraph_(isolate(), graph(), common(), &javascript_, &simplified_,
     29                  &machine_) {}
     30 
     31   JSGraph* jsgraph() { return &jsgraph_; }
     32   SimplifiedOperatorBuilder* simplified() { return &simplified_; }
     33 
     34  private:
     35   MachineOperatorBuilder machine_;
     36   JSOperatorBuilder javascript_;
     37   SimplifiedOperatorBuilder simplified_;
     38   JSGraph jsgraph_;
     39 };
     40 
     41 namespace {
     42 
     43 BasicBlock* AddBlockToSchedule(Schedule* schedule) {
     44   BasicBlock* block = schedule->NewBasicBlock();
     45   block->set_rpo_number(static_cast<int32_t>(schedule->rpo_order()->size()));
     46   schedule->rpo_order()->push_back(block);
     47   return block;
     48 }
     49 
     50 }  // namespace
     51 
     52 TEST_F(EffectControlLinearizerTest, SimpleLoad) {
     53   Schedule schedule(zone());
     54 
     55   // Create the graph.
     56   Node* heap_number = NumberConstant(0.5);
     57   Node* load = graph()->NewNode(
     58       simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), heap_number,
     59       graph()->start(), graph()->start());
     60   Node* ret = graph()->NewNode(common()->Return(), load, graph()->start(),
     61                                graph()->start());
     62 
     63   // Build the basic block structure.
     64   BasicBlock* start = schedule.start();
     65   schedule.rpo_order()->push_back(start);
     66   start->set_rpo_number(0);
     67 
     68   // Populate the basic blocks with nodes.
     69   schedule.AddNode(start, graph()->start());
     70   schedule.AddNode(start, heap_number);
     71   schedule.AddNode(start, load);
     72   schedule.AddReturn(start, ret);
     73 
     74   // Run the state effect introducer.
     75   EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
     76   introducer.Run();
     77 
     78   EXPECT_THAT(load,
     79               IsLoadField(AccessBuilder::ForHeapNumberValue(), heap_number,
     80                           graph()->start(), graph()->start()));
     81   // The return should have reconnected effect edge to the load.
     82   EXPECT_THAT(ret, IsReturn(load, load, graph()->start()));
     83 }
     84 
     85 TEST_F(EffectControlLinearizerTest, DiamondLoad) {
     86   Schedule schedule(zone());
     87 
     88   // Create the graph.
     89   Node* branch =
     90       graph()->NewNode(common()->Branch(), Int32Constant(0), graph()->start());
     91 
     92   Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
     93   Node* heap_number = NumberConstant(0.5);
     94   Node* vtrue = graph()->NewNode(
     95       simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), heap_number,
     96       graph()->start(), if_true);
     97 
     98   Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
     99   Node* vfalse = Float64Constant(2);
    100 
    101   Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
    102   Node* phi = graph()->NewNode(
    103       common()->Phi(MachineRepresentation::kFloat64, 2), vtrue, vfalse, merge);
    104 
    105   Node* ret =
    106       graph()->NewNode(common()->Return(), phi, graph()->start(), merge);
    107 
    108   // Build the basic block structure.
    109   BasicBlock* start = schedule.start();
    110   schedule.rpo_order()->push_back(start);
    111   start->set_rpo_number(0);
    112 
    113   BasicBlock* tblock = AddBlockToSchedule(&schedule);
    114   BasicBlock* fblock = AddBlockToSchedule(&schedule);
    115   BasicBlock* mblock = AddBlockToSchedule(&schedule);
    116 
    117   // Populate the basic blocks with nodes.
    118   schedule.AddNode(start, graph()->start());
    119   schedule.AddBranch(start, branch, tblock, fblock);
    120 
    121   schedule.AddNode(tblock, if_true);
    122   schedule.AddNode(tblock, heap_number);
    123   schedule.AddNode(tblock, vtrue);
    124   schedule.AddGoto(tblock, mblock);
    125 
    126   schedule.AddNode(fblock, if_false);
    127   schedule.AddNode(fblock, vfalse);
    128   schedule.AddGoto(fblock, mblock);
    129 
    130   schedule.AddNode(mblock, merge);
    131   schedule.AddNode(mblock, phi);
    132   schedule.AddReturn(mblock, ret);
    133 
    134   // Run the state effect introducer.
    135   EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
    136   introducer.Run();
    137 
    138   // The effect input to the return should be an effect phi with the
    139   // newly introduced effectful change operators.
    140   ASSERT_THAT(
    141       ret, IsReturn(phi, IsEffectPhi(vtrue, graph()->start(), merge), merge));
    142 }
    143 
    144 TEST_F(EffectControlLinearizerTest, FloatingDiamondsControlWiring) {
    145   Schedule schedule(zone());
    146 
    147   // Create the graph and schedule. Roughly (omitting effects and unimportant
    148   // nodes):
    149   //
    150   //            BLOCK 0:
    151   //             r1: Start
    152   //             c1: Call
    153   //             b1: Branch(const0, s1)
    154   //                |
    155   //        +-------+------+
    156   //        |              |
    157   //   BLOCK 1:           BLOCK 2:
    158   //    t1: IfTrue(b1)     f1: IfFalse(b1)
    159   //        |              |
    160   //        +-------+------+
    161   //                |
    162   //            BLOCK 3:
    163   //             m1: Merge(t1, f1)
    164   //             c2: IfSuccess(c1)
    165   //             b2: Branch(const0 , s1)
    166   //                |
    167   //        +-------+------+
    168   //        |              |
    169   //   BLOCK 4:           BLOCK 5:
    170   //    t2: IfTrue(b2)     f2:IfFalse(b2)
    171   //        |              |
    172   //        +-------+------+
    173   //                |
    174   //            BLOCK 6:
    175   //             m2: Merge(t2, f2)
    176   //             r1: Return(c1, c2)
    177   MachineType kMachineSignature[] = {MachineType::AnyTagged(),
    178                                      MachineType::AnyTagged()};
    179   LinkageLocation kLocationSignature[] = {LinkageLocation::ForRegister(0),
    180                                           LinkageLocation::ForRegister(1)};
    181   const CallDescriptor* kCallDescriptor = new (zone()) CallDescriptor(
    182       CallDescriptor::kCallCodeObject, MachineType::AnyTagged(),
    183       LinkageLocation::ForRegister(0),
    184       new (zone()) MachineSignature(1, 1, kMachineSignature),
    185       new (zone()) LocationSignature(1, 1, kLocationSignature), 0,
    186       Operator::kNoProperties, 0, 0, CallDescriptor::kNoFlags);
    187   Node* p0 = Parameter(0);
    188   Node* p1 = Parameter(1);
    189   Node* const0 = Int32Constant(0);
    190   Node* call = graph()->NewNode(common()->Call(kCallDescriptor), p0, p1,
    191                                 graph()->start(), graph()->start());
    192   Node* if_success = graph()->NewNode(common()->IfSuccess(), call);
    193 
    194   // First Floating diamond.
    195   Node* branch1 =
    196       graph()->NewNode(common()->Branch(), const0, graph()->start());
    197   Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
    198   Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
    199   Node* merge1 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
    200 
    201   // Second floating diamond.
    202   Node* branch2 =
    203       graph()->NewNode(common()->Branch(), const0, graph()->start());
    204   Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
    205   Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
    206   Node* merge2 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
    207 
    208   Node* ret =
    209       graph()->NewNode(common()->Return(), call, graph()->start(), if_success);
    210 
    211   // Build the basic block structure.
    212   BasicBlock* start = schedule.start();
    213   schedule.rpo_order()->push_back(start);
    214   start->set_rpo_number(0);
    215 
    216   BasicBlock* t1block = AddBlockToSchedule(&schedule);
    217   BasicBlock* f1block = AddBlockToSchedule(&schedule);
    218   BasicBlock* m1block = AddBlockToSchedule(&schedule);
    219 
    220   BasicBlock* t2block = AddBlockToSchedule(&schedule);
    221   BasicBlock* f2block = AddBlockToSchedule(&schedule);
    222   BasicBlock* m2block = AddBlockToSchedule(&schedule);
    223 
    224   // Populate the basic blocks with nodes.
    225   schedule.AddNode(start, graph()->start());
    226   schedule.AddNode(start, p0);
    227   schedule.AddNode(start, p1);
    228   schedule.AddNode(start, const0);
    229   schedule.AddNode(start, call);
    230   schedule.AddBranch(start, branch1, t1block, f1block);
    231 
    232   schedule.AddNode(t1block, if_true1);
    233   schedule.AddGoto(t1block, m1block);
    234 
    235   schedule.AddNode(f1block, if_false1);
    236   schedule.AddGoto(f1block, m1block);
    237 
    238   schedule.AddNode(m1block, merge1);
    239   // The scheduler does not always put the IfSuccess node to the corresponding
    240   // call's block, simulate that here.
    241   schedule.AddNode(m1block, if_success);
    242   schedule.AddBranch(m1block, branch2, t2block, f2block);
    243 
    244   schedule.AddNode(t2block, if_true2);
    245   schedule.AddGoto(t2block, m2block);
    246 
    247   schedule.AddNode(f2block, if_false2);
    248   schedule.AddGoto(f2block, m2block);
    249 
    250   schedule.AddNode(m2block, merge2);
    251   schedule.AddReturn(m2block, ret);
    252 
    253   // Run the state effect introducer.
    254   EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
    255   introducer.Run();
    256 
    257   // The effect input to the return should be an effect phi with the
    258   // newly introduced effectful change operators.
    259   ASSERT_THAT(ret, IsReturn(call, call, merge2));
    260   ASSERT_THAT(branch2, IsBranch(const0, merge1));
    261   ASSERT_THAT(branch1, IsBranch(const0, if_success));
    262   ASSERT_THAT(if_success, IsIfSuccess(call));
    263 }
    264 
    265 TEST_F(EffectControlLinearizerTest, LoopLoad) {
    266   Schedule schedule(zone());
    267 
    268   // Create the graph.
    269   Node* loop = graph()->NewNode(common()->Loop(1), graph()->start());
    270   Node* effect_phi =
    271       graph()->NewNode(common()->EffectPhi(1), graph()->start(), loop);
    272 
    273   Node* cond = Int32Constant(0);
    274   Node* branch = graph()->NewNode(common()->Branch(), cond, loop);
    275 
    276   Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
    277 
    278   Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    279 
    280   loop->AppendInput(zone(), if_false);
    281   NodeProperties::ChangeOp(loop, common()->Loop(2));
    282 
    283   effect_phi->InsertInput(zone(), 1, effect_phi);
    284   NodeProperties::ChangeOp(effect_phi, common()->EffectPhi(2));
    285 
    286   Node* heap_number = NumberConstant(0.5);
    287   Node* load = graph()->NewNode(
    288       simplified()->LoadField(AccessBuilder::ForHeapNumberValue()), heap_number,
    289       graph()->start(), loop);
    290 
    291   Node* ret = graph()->NewNode(common()->Return(), load, effect_phi, if_true);
    292 
    293   // Build the basic block structure.
    294   BasicBlock* start = schedule.start();
    295   schedule.rpo_order()->push_back(start);
    296   start->set_rpo_number(0);
    297 
    298   BasicBlock* lblock = AddBlockToSchedule(&schedule);
    299   BasicBlock* fblock = AddBlockToSchedule(&schedule);
    300   BasicBlock* rblock = AddBlockToSchedule(&schedule);
    301 
    302   // Populate the basic blocks with nodes.
    303   schedule.AddNode(start, graph()->start());
    304   schedule.AddGoto(start, lblock);
    305 
    306   schedule.AddNode(lblock, loop);
    307   schedule.AddNode(lblock, effect_phi);
    308   schedule.AddNode(lblock, heap_number);
    309   schedule.AddNode(lblock, load);
    310   schedule.AddNode(lblock, cond);
    311   schedule.AddBranch(lblock, branch, rblock, fblock);
    312 
    313   schedule.AddNode(fblock, if_false);
    314   schedule.AddGoto(fblock, lblock);
    315 
    316   schedule.AddNode(rblock, if_true);
    317   schedule.AddReturn(rblock, ret);
    318 
    319   // Run the state effect introducer.
    320   EffectControlLinearizer introducer(jsgraph(), &schedule, zone());
    321   introducer.Run();
    322 
    323   ASSERT_THAT(ret, IsReturn(load, load, if_true));
    324   EXPECT_THAT(load, IsLoadField(AccessBuilder::ForHeapNumberValue(),
    325                                 heap_number, effect_phi, loop));
    326 }
    327 
    328 }  // namespace compiler
    329 }  // namespace internal
    330 }  // namespace v8
    331