1 // Copyright 2018 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/torque/cfg.h"
6 
7 #include "src/torque/type-oracle.h"
8 
9 namespace v8 {
10 namespace internal {
11 namespace torque {
12 
SetInputTypes(const Stack<const Type * > & input_types)13 void Block::SetInputTypes(const Stack<const Type*>& input_types) {
14   if (!input_types_) {
15     input_types_ = input_types;
16     return;
17   } else if (*input_types_ == input_types) {
18     return;
19   }
20 
21   DCHECK_EQ(input_types.Size(), input_types_->Size());
22   Stack<const Type*> merged_types;
23   bool widened = false;
24   auto c2_iterator = input_types.begin();
25   for (const Type* c1 : *input_types_) {
26     const Type* merged_type = TypeOracle::GetUnionType(c1, *c2_iterator++);
27     if (!merged_type->IsSubtypeOf(c1)) {
28       widened = true;
29     }
30     merged_types.Push(merged_type);
31   }
32   if (merged_types.Size() == input_types_->Size()) {
33     if (widened) {
34       input_types_ = merged_types;
35       Retype();
36     }
37     return;
38   }
39 
40   std::stringstream error;
41   error << "incompatible types at branch:\n";
42   for (intptr_t i = std::max(input_types_->Size(), input_types.Size()) - 1;
43        i >= 0; --i) {
44     base::Optional<const Type*> left;
45     base::Optional<const Type*> right;
46     if (static_cast<size_t>(i) < input_types.Size()) {
47       left = input_types.Peek(BottomOffset{static_cast<size_t>(i)});
48     }
49     if (static_cast<size_t>(i) < input_types_->Size()) {
50       right = input_types_->Peek(BottomOffset{static_cast<size_t>(i)});
51     }
52     if (left && right && *left == *right) {
53       error << **left << "\n";
54     } else {
55       if (left) {
56         error << **left;
57       } else {
58         error << "/*missing*/";
59       }
60       error << "   =>   ";
61       if (right) {
62         error << **right;
63       } else {
64         error << "/*missing*/";
65       }
66       error << "\n";
67     }
68   }
69   ReportError(error.str());
70 }
71 
Bind(Block * block)72 void CfgAssembler::Bind(Block* block) {
73   DCHECK(current_block_->IsComplete());
74   DCHECK(block->instructions().empty());
75   DCHECK(block->HasInputTypes());
76   current_block_ = block;
77   current_stack_ = block->InputTypes();
78   cfg_.PlaceBlock(block);
79 }
80 
Goto(Block * block)81 void CfgAssembler::Goto(Block* block) {
82   if (block->HasInputTypes()) {
83     DropTo(block->InputTypes().AboveTop());
84   }
85   Emit(GotoInstruction{block});
86 }
87 
Goto(Block * block,size_t preserved_slots)88 StackRange CfgAssembler::Goto(Block* block, size_t preserved_slots) {
89   DCHECK(block->HasInputTypes());
90   DCHECK_GE(CurrentStack().Size(), block->InputTypes().Size());
91   Emit(DeleteRangeInstruction{
92       StackRange{block->InputTypes().AboveTop() - preserved_slots,
93                  CurrentStack().AboveTop() - preserved_slots}});
94   StackRange preserved_slot_range = TopRange(preserved_slots);
95   Emit(GotoInstruction{block});
96   return preserved_slot_range;
97 }
98 
Branch(Block * if_true,Block * if_false)99 void CfgAssembler::Branch(Block* if_true, Block* if_false) {
100   Emit(BranchInstruction{if_true, if_false});
101 }
102 
103 // Delete the specified range of slots, moving upper slots to fill the gap.
DeleteRange(StackRange range)104 void CfgAssembler::DeleteRange(StackRange range) {
105   DCHECK_LE(range.end(), current_stack_.AboveTop());
106   if (range.Size() == 0) return;
107   Emit(DeleteRangeInstruction{range});
108 }
109 
DropTo(BottomOffset new_level)110 void CfgAssembler::DropTo(BottomOffset new_level) {
111   DeleteRange(StackRange{new_level, CurrentStack().AboveTop()});
112 }
113 
Peek(StackRange range,base::Optional<const Type * > type)114 StackRange CfgAssembler::Peek(StackRange range,
115                               base::Optional<const Type*> type) {
116   std::vector<const Type*> lowered_types;
117   if (type) {
118     lowered_types = LowerType(*type);
119     DCHECK_EQ(lowered_types.size(), range.Size());
120   }
121   for (size_t i = 0; i < range.Size(); ++i) {
122     Emit(PeekInstruction{
123         range.begin() + i,
124         type ? lowered_types[i] : base::Optional<const Type*>{}});
125   }
126   return TopRange(range.Size());
127 }
128 
Poke(StackRange destination,StackRange origin,base::Optional<const Type * > type)129 void CfgAssembler::Poke(StackRange destination, StackRange origin,
130                         base::Optional<const Type*> type) {
131   DCHECK_EQ(destination.Size(), origin.Size());
132   DCHECK_LE(destination.end(), origin.begin());
133   DCHECK_EQ(origin.end(), CurrentStack().AboveTop());
134   std::vector<const Type*> lowered_types;
135   if (type) {
136     lowered_types = LowerType(*type);
137     DCHECK_EQ(lowered_types.size(), origin.Size());
138   }
139   for (intptr_t i = origin.Size() - 1; i >= 0; --i) {
140     Emit(PokeInstruction{
141         destination.begin() + i,
142         type ? lowered_types[i] : base::Optional<const Type*>{}});
143   }
144 }
145 
Print(std::string s)146 void CfgAssembler::Print(std::string s) {
147   Emit(PrintConstantStringInstruction{std::move(s)});
148 }
149 
AssertionFailure(std::string message)150 void CfgAssembler::AssertionFailure(std::string message) {
151   Emit(AbortInstruction{AbortInstruction::Kind::kAssertionFailure,
152                         std::move(message)});
153 }
154 
Unreachable()155 void CfgAssembler::Unreachable() {
156   Emit(AbortInstruction{AbortInstruction::Kind::kUnreachable});
157 }
158 
DebugBreak()159 void CfgAssembler::DebugBreak() {
160   Emit(AbortInstruction{AbortInstruction::Kind::kDebugBreak});
161 }
162 
CountBlockPredecessors(const ControlFlowGraph & cfg)163 std::vector<std::size_t> CountBlockPredecessors(const ControlFlowGraph& cfg) {
164   std::vector<std::size_t> count(cfg.NumberOfBlockIds(), 0);
165   count[cfg.start()->id()] = 1;
166 
167   for (const Block* block : cfg.blocks()) {
168     std::vector<Block*> successors;
169     for (const auto& instruction : block->instructions()) {
170       instruction->AppendSuccessorBlocks(&successors);
171     }
172     for (Block* successor : successors) {
173       DCHECK_LT(successor->id(), count.size());
174       ++count[successor->id()];
175     }
176   }
177 
178   return count;
179 }
180 
OptimizeCfg()181 void CfgAssembler::OptimizeCfg() {
182   auto predecessor_count = CountBlockPredecessors(cfg_);
183 
184   for (Block* block : cfg_.blocks()) {
185     if (cfg_.end() && *cfg_.end() == block) continue;
186     if (predecessor_count[block->id()] == 0) continue;
187 
188     while (!block->instructions().empty()) {
189       const auto& instruction = block->instructions().back();
190       if (!instruction.Is<GotoInstruction>()) break;
191       Block* destination = instruction.Cast<GotoInstruction>().destination;
192       if (destination == block) break;
193       if (cfg_.end() && *cfg_.end() == destination) break;
194       DCHECK_GT(predecessor_count[destination->id()], 0);
195       if (predecessor_count[destination->id()] != 1) break;
196 
197       DCHECK_GT(destination->instructions().size(), 0);
198       block->instructions().pop_back();
199       block->instructions().insert(block->instructions().end(),
200                                    destination->instructions().begin(),
201                                    destination->instructions().end());
202 
203       --predecessor_count[destination->id()];
204       DCHECK_EQ(predecessor_count[destination->id()], 0);
205     }
206   }
207 
208   cfg_.UnplaceBlockIf(
209       [&](Block* b) { return predecessor_count[b->id()] == 0; });
210 }
211 
ComputeInputDefinitions()212 void CfgAssembler::ComputeInputDefinitions() {
213   Worklist<Block*> worklist;
214 
215   // Setup start block.
216   Stack<DefinitionLocation> parameter_defs;
217   for (std::size_t i = 0; i < cfg_.ParameterCount(); ++i) {
218     parameter_defs.Push(DefinitionLocation::Parameter(i));
219   }
220   cfg_.start()->MergeInputDefinitions(parameter_defs, &worklist);
221 
222   // Run fixpoint algorithm.
223   while (!worklist.IsEmpty()) {
224     Block* block = worklist.Dequeue();
225     Stack<DefinitionLocation> definitions = block->InputDefinitions();
226 
227     // Propagate through block's instructions.
228     for (const auto& instruction : block->instructions()) {
229       instruction.RecomputeDefinitionLocations(&definitions, &worklist);
230     }
231   }
232 
233   for (Block* block : cfg_.blocks()) {
234     DCHECK_IMPLIES(!block->IsDead(), block->InputDefinitions().Size() ==
235                                          block->InputTypes().Size());
236     USE(block);
237   }
238 }
239 
240 }  // namespace torque
241 }  // namespace internal
242 }  // namespace v8
243