Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
il_test_helper.h
Go to the documentation of this file.
1// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
6#define RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
7
8#include <memory>
9#include <type_traits>
10#include <utility>
11#include <vector>
12
13#include "include/dart_api.h"
14
15#include "platform/allocation.h"
22#include "vm/unit_test.h"
23
24// The helpers in this file make it easier to write C++ unit tests which assert
25// that Dart code gets turned into certain IR.
26//
27// Here is an example on how to use it:
28//
29// ISOLATE_UNIT_TEST_CASE(MyIRTest) {
30// const char* script = R"(
31// void foo() { ... }
32// void main() { foo(); }
33// )";
34//
35// // Load the script and exercise the code once.
36// const auto& lib = Library::Handle(LoadTestScript(script);
37//
38// // Cause the code to be exercised once (to populate ICData).
39// Invoke(lib, "main");
40//
41// // Look up the function.
42// const auto& function = Function::Handle(GetFunction(lib, "foo"));
43//
44// // Run the JIT compilation pipeline with two passes.
45// TestPipeline pipeline(function);
46// FlowGraph* graph = pipeline.RunJITPasses("ComputeSSA,TypePropagation");
47//
48// ...
49// }
50//
51namespace dart {
52
53class FlowGraph;
54class Function;
55class Library;
56
57LibraryPtr LoadTestScript(const char* script,
58 Dart_NativeEntryResolver resolver = nullptr,
59 const char* lib_uri = RESOLVED_USER_TEST_URI);
60
61NOT_IN_PRODUCT(LibraryPtr ReloadTestScript(const char* script));
62
63FunctionPtr GetFunction(const Library& lib, const char* name);
64ClassPtr GetClass(const Library& lib, const char* name);
65TypeParameterPtr GetClassTypeParameter(const Class& klass, intptr_t index);
66TypeParameterPtr GetFunctionTypeParameter(const Function& fun, intptr_t index);
67
68ObjectPtr Invoke(const Library& lib, const char* name);
69
70InstructionsPtr BuildInstructions(
71 std::function<void(compiler::Assembler* assembler)> fun);
72
73class TestPipeline : public ValueObject {
74 public:
77 bool is_optimizing = true)
78 : function_(function),
79 thread_(Thread::Current()),
80 compiler_state_(thread_,
81 mode == CompilerPass::PipelineMode::kAOT,
82 is_optimizing,
83 CompilerState::ShouldTrace(function)),
84 hierarchy_info_(thread_),
85 speculative_policy_(std::unique_ptr<SpeculativeInliningPolicy>(
86 new SpeculativeInliningPolicy(/*enable_suppresson=*/false))),
87 mode_(mode) {}
88 ~TestPipeline() { delete pass_state_; }
89
90 // As a side-effect this will populate
91 // - [ic_data_array_]
92 // - [parsed_function_]
93 // - [pass_state_]
94 // - [flow_graph_]
95 FlowGraph* RunPasses(std::initializer_list<CompilerPass::Id> passes);
96
97 void RunAdditionalPasses(std::initializer_list<CompilerPass::Id> passes);
98
100
102
103 private:
104 const Function& function_;
105 Thread* thread_;
106 CompilerState compiler_state_;
107 HierarchyInfo hierarchy_info_;
108 std::unique_ptr<SpeculativeInliningPolicy> speculative_policy_;
110 ZoneGrowableArray<const ICData*>* ic_data_array_ = nullptr;
111 ParsedFunction* parsed_function_ = nullptr;
112 CompilerPassState* pass_state_ = nullptr;
113 FlowGraph* flow_graph_ = nullptr;
114};
115
116// Match opcodes used for [ILMatcher], see below.
118// Emit a match and match-and-move code for every instruction.
119#define DEFINE_MATCH_OPCODES(Instruction, _) \
120 kMatch##Instruction, kMatchAndMove##Instruction, \
121 kMatchAndMoveOptional##Instruction,
124#undef DEFINE_MATCH_OPCODES
125
126 // Matches a branch and moves left.
128
129 // Matches a branch and moves right.
131
132 // Is ignored.
134
135 // Moves forward across any instruction.
137
138 // Moves over all parallel moves.
140
141 // Moves forward until the next match code matches.
143
144 // Moves over any DebugStepChecks.
146
147 // Invalid match opcode used as default [insert_before] argument to TryMatch
148 // to signal that no insertions should occur.
150};
151
152// Match codes used for [ILMatcher], see below.
154 public:
156 : opcode_(opcode), capture_(nullptr) {}
157
159 : opcode_(opcode), capture_(capture) {}
160
161#define DEFINE_TYPED_CONSTRUCTOR(Type, ignored) \
162 MatchCode(MatchOpCode opcode, Type##Instr** capture) \
163 : opcode_(opcode), capture_(reinterpret_cast<Instruction**>(capture)) { \
164 RELEASE_ASSERT(opcode == kMatch##Type || opcode == kMatchAndMove##Type); \
165 }
168#undef DEFINE_TYPED_CONSTRUCTOR
169
170 MatchOpCode opcode() { return opcode_; }
171
172 private:
173 friend class ILMatcher;
174
175 MatchOpCode opcode_;
176 Instruction** capture_;
177};
178
180 // Matcher doesn't do anything special with ParallelMove instructions.
181 kDefault,
182 // All ParallelMove instructions are skipped.
183 // This mode is useful when matching a flow graph after the whole
184 // compiler pipeline, as it may have ParallelMove instructions
185 // at arbitrary architecture-dependent places.
186 kSkip,
187};
188
189// Used for matching a sequence of IL instructions including capturing support.
190//
191// Example:
192//
193// TargetEntryInstr* entry = ....;
194// BranchInstr* branch = nullptr;
195//
196// ILMatcher matcher(flow_graph, entry);
197// if (matcher.TryMatch({ kMoveGlob, {kMatchBranch, &branch}, })) {
198// EXPECT(branch->operation_cid() == kMintCid);
199// ...
200// }
201//
202// This match will start at [entry], follow any number instructions (including
203// [GotoInstr]s until a [BranchInstr] is found).
204//
205// If the match was successful, this returns `true` and updates the current
206// value for the cursor.
207class ILMatcher : public ValueObject {
208 public:
209 ILMatcher(FlowGraph* flow_graph,
210 Instruction* cursor,
211 bool trace = true,
212 ParallelMovesHandling parallel_moves_handling =
214 : flow_graph_(flow_graph),
215 cursor_(cursor),
216 parallel_moves_handling_(parallel_moves_handling),
217 // clang-format off
218#if !defined(PRODUCT)
219 trace_(trace) {}
220#else
221 trace_(false) {}
222#endif
223 // clang-format on
224
225 Instruction* value() { return cursor_; }
226
227 // From the current [value] according to match_codes.
228 //
229 // Returns `true` if the match was successful and cursor has been updated,
230 // otherwise returns `false`.
231 //
232 // If [insert_before] is a valid match opcode, then it will be inserted
233 // before each MatchCode in [match_codes] prior to matching.
234 bool TryMatch(std::initializer_list<MatchCode> match_codes,
235 MatchOpCode insert_before = kInvalidMatchOpCode);
236
237 private:
238 Instruction* MatchInternal(std::vector<MatchCode> match_codes,
239 size_t i,
240 Instruction* cursor);
241
242 const char* MatchOpCodeToCString(MatchOpCode code);
243
244 FlowGraph* flow_graph_;
245 Instruction* cursor_;
246 ParallelMovesHandling parallel_moves_handling_;
247 bool trace_;
248};
249
250#if !defined(PRODUCT)
251#define ENTITY_TOCSTRING(v) ((v)->ToCString())
252#else
253#define ENTITY_TOCSTRING(v) "<?>"
254#endif
255
256// Helper to check various IL and object properties and informative error
257// messages if check fails. [entity] should be a pointer to a value.
258// [property] should be an expression which can refer to [entity] using
259// variable named [it].
260// [entity] is expected to have a ToCString() method in non-PRODUCT builds.
261#define EXPECT_PROPERTY(entity, property) \
262 do { \
263 auto& it = *entity; \
264 if (!(property)) { \
265 dart::Expect(__FILE__, __LINE__) \
266 .Fail("expected " #property " for " #entity " which is %s.\n", \
267 ENTITY_TOCSTRING(entity)); \
268 } \
269 } while (0)
270
272 public:
273 explicit FlowGraphBuilderHelper(intptr_t num_parameters = 0)
274 : state_(CompilerState::Current()),
275 flow_graph_(MakeDummyGraph(Thread::Current(),
276 num_parameters,
277 state_.is_optimizing())) {
278 flow_graph_.CreateCommonConstants();
279 }
280
281 TargetEntryInstr* TargetEntry(intptr_t try_index = kInvalidTryIndex) const {
282 return new TargetEntryInstr(flow_graph_.allocate_block_id(), try_index,
283 state_.GetNextDeoptId());
284 }
285
286 JoinEntryInstr* JoinEntry(intptr_t try_index = kInvalidTryIndex) const {
287 return new JoinEntryInstr(flow_graph_.allocate_block_id(), try_index,
288 state_.GetNextDeoptId());
289 }
290
291 ConstantInstr* IntConstant(int64_t value) const {
292 return flow_graph_.GetConstant(
294 }
295
298 }
299
300 // Adds a variable into the scope which would provide inferred argument type
301 // for the parameter.
302 void AddVariable(const char* name,
303 const AbstractType& static_type,
304 CompileType* inferred_arg_type = nullptr) {
305 LocalVariable* v =
306 new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
310 static_type, CompileType::kCanBeNull,
312 inferred_arg_type);
315 }
316
317 enum class IncomingDefKind {
319 kDelayed,
320 };
321
323 public:
326
327 template <typename T,
328 typename = typename std::enable_if<
329 std::is_base_of<Definition, T>::value>::type>
331 : kind_(IncomingDefKind::kDelayed),
332 from_(from),
333 defn_source_(reinterpret_cast<Definition**>(defn_source)) {}
334
335 BlockEntryInstr* from() const { return from_; }
336 Definition* defn() const {
338 }
339
340 private:
341 IncomingDefKind kind_;
342 BlockEntryInstr* from_;
343 union {
346 };
347 };
348
350 std::initializer_list<IncomingDef> incoming) {
351 auto phi = new PhiInstr(join, incoming.size());
352 for (size_t i = 0; i < incoming.size(); i++) {
353 auto input = new Value(flow_graph_.constant_dead());
354 phi->SetInputAt(i, input);
355 input->definition()->AddInputUse(input);
356 }
357 for (auto def : incoming) {
358 pending_phis_.Add({phi, def});
359 }
360 return phi;
361 }
362
363 void FinishGraph() {
364 flow_graph_.DiscoverBlocks();
365 GrowableArray<BitVector*> dominance_frontier;
366 flow_graph_.ComputeDominators(&dominance_frontier);
367
368 for (auto& pending : pending_phis_) {
369 auto join = pending.phi->block();
370 EXPECT(pending.phi->InputCount() == join->PredecessorCount());
371 auto pred_index = join->IndexOfPredecessor(pending.incoming.from());
372 EXPECT(pred_index != -1);
373 pending.phi->InputAt(pred_index)->BindTo(pending.incoming.defn());
374 }
375 }
376
377 FlowGraph* flow_graph() { return &flow_graph_; }
378
379 private:
380 static FlowGraph& MakeDummyGraph(Thread* thread,
381 intptr_t num_parameters,
382 bool is_optimizing) {
383 const FunctionType& signature =
385 signature.set_num_fixed_parameters(num_parameters);
387 signature, String::Handle(Symbols::New(thread, "dummy")),
388 UntaggedFunction::kRegularFunction,
389 /*is_static=*/true,
390 /*is_const=*/false,
391 /*is_abstract=*/false,
392 /*is_external=*/false,
393 /*is_native=*/true,
394 Class::Handle(thread->isolate_group()->object_store()->object_class()),
395 TokenPosition::kNoSource));
396
397 Zone* zone = thread->zone();
398 ParsedFunction* parsed_function = new (zone) ParsedFunction(thread, func);
399
400 parsed_function->set_scope(new LocalScope(nullptr, 0, 0));
401
402 auto graph_entry =
403 new GraphEntryInstr(*parsed_function, Compiler::kNoOSRDeoptId);
404
405 const intptr_t block_id = 1; // 0 is GraphEntry.
406 graph_entry->set_normal_entry(
407 new FunctionEntryInstr(graph_entry, block_id, kInvalidTryIndex,
408 CompilerState::Current().GetNextDeoptId()));
409 return *new FlowGraph(*parsed_function, graph_entry, block_id,
410 PrologueInfo{-1, -1},
411 FlowGraph::CompilationModeFrom(is_optimizing));
412 }
413
414 CompilerState& state_;
415 FlowGraph& flow_graph_;
416
417 struct PendingPhiInput {
418 PhiInstr* phi;
419 IncomingDef incoming;
420 };
421 GrowableArray<PendingPhiInput> pending_phis_;
422};
423
424} // namespace dart
425
426#endif // RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
#define EXPECT(type, expectedAlignment, expectedSize)
void Add(const T &value)
static constexpr bool kCannotBeSentinel
static constexpr bool kCanBeNull
static CompileType FromAbstractType(const AbstractType &type, bool can_be_null, bool can_be_sentinel)
intptr_t GetNextDeoptId()
static CompilerState & Current()
static constexpr intptr_t kNoOSRDeoptId
Definition compiler.h:73
static DoublePtr NewCanonical(double d)
Definition object.cc:23497
IncomingDef(BlockEntryInstr *from, T **defn_source)
IncomingDef(BlockEntryInstr *from, Definition *defn)
JoinEntryInstr * JoinEntry(intptr_t try_index=kInvalidTryIndex) const
PhiInstr * Phi(JoinEntryInstr *join, std::initializer_list< IncomingDef > incoming)
void AddVariable(const char *name, const AbstractType &static_type, CompileType *inferred_arg_type=nullptr)
ConstantInstr * IntConstant(int64_t value) const
TargetEntryInstr * TargetEntry(intptr_t try_index=kInvalidTryIndex) const
ConstantInstr * DoubleConstant(double value)
FlowGraphBuilderHelper(intptr_t num_parameters=0)
ConstantInstr * GetConstant(const Object &object, Representation representation=kTagged)
static constexpr CompilationMode CompilationModeFrom(bool is_optimizing)
Definition flow_graph.h:587
ConstantInstr * constant_dead() const
Definition flow_graph.h:272
void CreateCommonConstants()
void DiscoverBlocks()
void ComputeDominators(GrowableArray< BitVector * > *dominance_frontier)
const ParsedFunction & parsed_function() const
Definition flow_graph.h:129
intptr_t allocate_block_id()
Definition flow_graph.h:266
void set_num_fixed_parameters(intptr_t value) const
Definition object.cc:11659
static FunctionTypePtr New(intptr_t num_parent_type_arguments=0, Nullability nullability=Nullability::kLegacy, Heap::Space space=Heap::kOld)
Definition object.cc:11682
static FunctionPtr New(const FunctionType &signature, const String &name, UntaggedFunction::Kind kind, bool is_static, bool is_const, bool is_abstract, bool is_external, bool is_native, const Object &owner, TokenPosition token_pos, Heap::Space space=Heap::kOld)
Definition object.cc:10301
ILMatcher(FlowGraph *flow_graph, Instruction *cursor, bool trace=true, ParallelMovesHandling parallel_moves_handling=ParallelMovesHandling::kDefault)
Instruction * value()
bool TryMatch(std::initializer_list< MatchCode > match_codes, MatchOpCode insert_before=kInvalidMatchOpCode)
static IntegerPtr NewCanonical(const String &str)
Definition object.cc:23078
ObjectStore * object_store() const
Definition isolate.h:505
bool AddVariable(LocalVariable *variable)
Definition scopes.cc:57
void set_type_check_mode(TypeCheckMode mode)
Definition scopes.h:199
static constexpr intptr_t kNoKernelOffset
Definition scopes.h:77
MatchCode(MatchOpCode opcode, Instruction **capture)
MatchCode(MatchOpCode opcode)
MatchOpCode opcode()
static Object & Handle()
Definition object.h:407
static Object & ZoneHandle()
Definition object.h:419
LocalScope * scope() const
Definition parser.h:76
void set_scope(LocalScope *scope)
Definition parser.h:77
static StringPtr New(Thread *thread, const char *cstr)
Definition symbols.h:722
void RunAdditionalPasses(std::initializer_list< CompilerPass::Id > passes)
FlowGraph * RunPasses(std::initializer_list< CompilerPass::Id > passes)
void CompileGraphAndAttachFunction()
TestPipeline(const Function &function, CompilerPass::PipelineMode mode, bool is_optimizing=true)
void RunForcedOptimizedAfterSSAPasses()
Zone * zone() const
static Thread * Current()
Definition thread.h:361
IsolateGroup * isolate_group() const
Definition thread.h:540
Dart_NativeFunction(* Dart_NativeEntryResolver)(Dart_Handle name, int num_of_arguments, bool *auto_setup_scope)
Definition dart_api.h:3225
if(end==-1)
uint8_t value
Dart_NativeFunction function
Definition fuchsia.cc:51
#define FOR_EACH_INSTRUCTION(M)
Definition il.h:405
#define FOR_EACH_ABSTRACT_INSTRUCTION(M)
Definition il.h:553
#define DEFINE_MATCH_OPCODES(Instruction, _)
#define DEFINE_TYPED_CONSTRUCTOR(Type, ignored)
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
@ kMatchAndMoveBranchFalse
@ kMoveDebugStepChecks
@ kInvalidMatchOpCode
@ kMatchAndMoveBranchTrue
@ kMoveParallelMoves
const char *const name
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
LibraryPtr ReloadTestScript(const char *script)
ParallelMovesHandling
ClassPtr GetClass(const Library &lib, const char *name)
TypeParameterPtr GetClassTypeParameter(const Class &klass, intptr_t index)
TypeParameterPtr GetFunctionTypeParameter(const Function &fun, intptr_t index)
InstructionsPtr BuildInstructions(std::function< void(compiler::Assembler *assembler)> fun)
static constexpr intptr_t kInvalidTryIndex
Definition ref_ptr.h:256
#define T
#define RESOLVED_USER_TEST_URI
Definition unit_test.h:317
#define NOT_IN_PRODUCT(code)
Definition globals.h:84