Flutter Engine
The Flutter Engine
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 : thread_(Thread::Current()),
79 compiler_state_(thread_,
80 mode == CompilerPass::PipelineMode::kAOT,
81 is_optimizing,
82 CompilerState::ShouldTrace(function)),
83 hierarchy_info_(thread_),
84 speculative_policy_(std::unique_ptr<SpeculativeInliningPolicy>(
85 new SpeculativeInliningPolicy(/*enable_suppresson=*/false))),
86 mode_(mode),
87 flow_graph_(nullptr),
88 function_(function),
89 parsed_function_(nullptr) {}
90 ~TestPipeline() { delete pass_state_; }
91
93 : thread_(Thread::Current()),
94 compiler_state_(thread_,
95 mode == CompilerPass::PipelineMode::kAOT,
96 /*is_optimizing=*/true,
98 hierarchy_info_(thread_),
99 speculative_policy_(std::unique_ptr<SpeculativeInliningPolicy>(
100 new SpeculativeInliningPolicy(/*enable_suppresson=*/false))),
101 mode_(mode),
102 flow_graph_(fn()),
103 function_(flow_graph_->function()),
104 parsed_function_(
105 const_cast<ParsedFunction*>(&flow_graph_->parsed_function())) {}
106
107 // As a side-effect this will populate
108 // - [ic_data_array_]
109 // - [parsed_function_]
110 // - [pass_state_]
111 // - [flow_graph_]
112 FlowGraph* RunPasses(std::initializer_list<CompilerPass::Id> passes);
113
114 void RunAdditionalPasses(std::initializer_list<CompilerPass::Id> passes);
115
117
119
120 private:
121 Thread* thread_;
122 CompilerState compiler_state_;
123 HierarchyInfo hierarchy_info_;
124 std::unique_ptr<SpeculativeInliningPolicy> speculative_policy_;
126 ZoneGrowableArray<const ICData*>* ic_data_array_ = nullptr;
127 CompilerPassState* pass_state_ = nullptr;
128 FlowGraph* flow_graph_;
129 const Function& function_;
130 ParsedFunction* parsed_function_;
131};
132
133// Match opcodes used for [ILMatcher], see below.
135// Emit a match and match-and-move code for every instruction.
136#define DEFINE_MATCH_OPCODES(Instruction, _) \
137 kMatch##Instruction, kMatchAndMove##Instruction, \
138 kMatchAndMoveOptional##Instruction,
141#undef DEFINE_MATCH_OPCODES
142
143 // Matches a branch and moves left.
145
146 // Matches a branch and moves right.
148
149 // Is ignored.
151
152 // Moves forward across any instruction.
154
155 // Moves over all parallel moves.
157
158 // Moves forward until the next match code matches.
160
161 // Moves over any DebugStepChecks.
163
164 // Invalid match opcode used as default [insert_before] argument to TryMatch
165 // to signal that no insertions should occur.
167};
168
169// Match codes used for [ILMatcher], see below.
171 public:
173 : opcode_(opcode), capture_(nullptr) {}
174
176 : opcode_(opcode), capture_(capture) {}
177
178#define DEFINE_TYPED_CONSTRUCTOR(Type, ignored) \
179 MatchCode(MatchOpCode opcode, Type##Instr** capture) \
180 : opcode_(opcode), capture_(reinterpret_cast<Instruction**>(capture)) { \
181 RELEASE_ASSERT(opcode == kMatch##Type || opcode == kMatchAndMove##Type); \
182 }
185#undef DEFINE_TYPED_CONSTRUCTOR
186
187 MatchOpCode opcode() { return opcode_; }
188
189 private:
190 friend class ILMatcher;
191
192 MatchOpCode opcode_;
193 Instruction** capture_;
194};
195
197 // Matcher doesn't do anything special with ParallelMove instructions.
198 kDefault,
199 // All ParallelMove instructions are skipped.
200 // This mode is useful when matching a flow graph after the whole
201 // compiler pipeline, as it may have ParallelMove instructions
202 // at arbitrary architecture-dependent places.
203 kSkip,
204};
205
206// Used for matching a sequence of IL instructions including capturing support.
207//
208// Example:
209//
210// TargetEntryInstr* entry = ....;
211// BranchInstr* branch = nullptr;
212//
213// ILMatcher matcher(flow_graph, entry);
214// if (matcher.TryMatch({ kMoveGlob, {kMatchBranch, &branch}, })) {
215// EXPECT(branch->operation_cid() == kMintCid);
216// ...
217// }
218//
219// This match will start at [entry], follow any number instructions (including
220// [GotoInstr]s until a [BranchInstr] is found).
221//
222// If the match was successful, this returns `true` and updates the current
223// value for the cursor.
224class ILMatcher : public ValueObject {
225 public:
226 ILMatcher(FlowGraph* flow_graph,
227 Instruction* cursor,
228 bool trace = true,
229 ParallelMovesHandling parallel_moves_handling =
231 : flow_graph_(flow_graph),
232 cursor_(cursor),
233 parallel_moves_handling_(parallel_moves_handling),
234 // clang-format off
235#if !defined(PRODUCT)
236 trace_(trace) {}
237#else
238 trace_(false) {}
239#endif
240 // clang-format on
241
242 Instruction* value() { return cursor_; }
243
244 // From the current [value] according to match_codes.
245 //
246 // Returns `true` if the match was successful and cursor has been updated,
247 // otherwise returns `false`.
248 //
249 // If [insert_before] is a valid match opcode, then it will be inserted
250 // before each MatchCode in [match_codes] prior to matching.
251 bool TryMatch(std::initializer_list<MatchCode> match_codes,
252 MatchOpCode insert_before = kInvalidMatchOpCode);
253
254 private:
255 Instruction* MatchInternal(std::vector<MatchCode> match_codes,
256 size_t i,
257 Instruction* cursor);
258
259 const char* MatchOpCodeToCString(MatchOpCode code);
260
261 FlowGraph* flow_graph_;
262 Instruction* cursor_;
263 ParallelMovesHandling parallel_moves_handling_;
264 bool trace_;
265};
266
267#if !defined(PRODUCT)
268#define ENTITY_TOCSTRING(v) ((v)->ToCString())
269#else
270#define ENTITY_TOCSTRING(v) "<?>"
271#endif
272
273// Helper to check various IL and object properties and informative error
274// messages if check fails. [entity] should be a pointer to a value.
275// [property] should be an expression which can refer to [entity] using
276// variable named [it].
277// [entity] is expected to have a ToCString() method in non-PRODUCT builds.
278#define EXPECT_PROPERTY(entity, property) \
279 do { \
280 auto& it = *entity; \
281 if (!(property)) { \
282 dart::Expect(__FILE__, __LINE__) \
283 .Fail("expected " #property " for " #entity " which is %s.\n", \
284 ENTITY_TOCSTRING(entity)); \
285 } \
286 } while (0)
287
289 public:
291 intptr_t num_parameters = 0,
292 const std::function<void(const Function&)>& configure_function =
293 [](const Function&) {})
294 : state_(CompilerState::Current()),
295 flow_graph_(MakeDummyGraph(Thread::Current(),
296 num_parameters,
297 state_.is_optimizing(),
298 configure_function)) {
299 flow_graph_.CreateCommonConstants();
300 }
301
302 TargetEntryInstr* TargetEntry(intptr_t try_index = kInvalidTryIndex) const {
303 return new TargetEntryInstr(flow_graph_.allocate_block_id(), try_index,
304 state_.GetNextDeoptId());
305 }
306
307 JoinEntryInstr* JoinEntry(intptr_t try_index = kInvalidTryIndex) const {
308 return new JoinEntryInstr(flow_graph_.allocate_block_id(), try_index,
309 state_.GetNextDeoptId());
310 }
311
313 Representation representation = kTagged) const {
314 ASSERT(representation == kTagged ||
316 return flow_graph_.GetConstant(
318 }
319
322 }
323
324 // Adds a variable into the scope which would provide inferred argument type
325 // for the parameter.
326 void AddVariable(const char* name,
327 const AbstractType& static_type,
328 CompileType* inferred_arg_type = nullptr) {
329 LocalVariable* v =
330 new LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
334 static_type, CompileType::kCanBeNull,
336 inferred_arg_type);
339 }
340
341 enum class IncomingDefKind {
342 kImmediate,
343 kDelayed,
344 };
345
347 public:
349 : kind_(IncomingDefKind::kImmediate), from_(from), defn_(defn) {}
350
351 template <typename T,
352 typename = typename std::enable_if<
355 : kind_(IncomingDefKind::kDelayed),
356 from_(from),
357 defn_source_(reinterpret_cast<Definition**>(defn_source)) {}
358
359 BlockEntryInstr* from() const { return from_; }
360 Definition* defn() const {
362 }
363
364 private:
365 IncomingDefKind kind_;
366 BlockEntryInstr* from_;
367 union {
370 };
371 };
372
374 std::initializer_list<IncomingDef> incoming) {
375 auto phi = new PhiInstr(join, incoming.size());
376 for (size_t i = 0; i < incoming.size(); i++) {
377 auto input = new Value(flow_graph_.constant_dead());
378 phi->SetInputAt(i, input);
379 input->definition()->AddInputUse(input);
380 }
381 for (auto def : incoming) {
382 pending_phis_.Add({phi, def});
383 }
384 return phi;
385 }
386
387 void FinishGraph() {
388 flow_graph_.DiscoverBlocks();
389 GrowableArray<BitVector*> dominance_frontier;
390 flow_graph_.ComputeDominators(&dominance_frontier);
391
392 for (auto& pending : pending_phis_) {
393 auto join = pending.phi->block();
394 EXPECT(pending.phi->InputCount() == join->PredecessorCount());
395 auto pred_index = join->IndexOfPredecessor(pending.incoming.from());
396 EXPECT(pred_index != -1);
397 pending.phi->InputAt(pred_index)->BindTo(pending.incoming.defn());
398 }
399 }
400
401 FlowGraph* flow_graph() { return &flow_graph_; }
402
403 private:
404 static FlowGraph& MakeDummyGraph(
405 Thread* thread,
406 intptr_t num_parameters,
407 bool is_optimizing,
408 const std::function<void(const Function&)>& configure_function) {
409 const FunctionType& signature =
411 signature.set_num_fixed_parameters(num_parameters);
413 signature, String::Handle(Symbols::New(thread, "dummy")),
414 UntaggedFunction::kRegularFunction,
415 /*is_static=*/true,
416 /*is_const=*/false,
417 /*is_abstract=*/false,
418 /*is_external=*/false,
419 /*is_native=*/true,
420 Class::Handle(thread->isolate_group()->object_store()->object_class()),
421 TokenPosition::kNoSource));
422 configure_function(func);
423
424 Zone* zone = thread->zone();
425 ParsedFunction* parsed_function = new (zone) ParsedFunction(thread, func);
426
427 parsed_function->set_scope(new LocalScope(nullptr, 0, 0));
428
429 auto graph_entry =
430 new GraphEntryInstr(*parsed_function, Compiler::kNoOSRDeoptId);
431
432 const intptr_t block_id = 1; // 0 is GraphEntry.
433 graph_entry->set_normal_entry(
434 new FunctionEntryInstr(graph_entry, block_id, kInvalidTryIndex,
435 CompilerState::Current().GetNextDeoptId()));
436 return *new FlowGraph(*parsed_function, graph_entry, block_id,
437 PrologueInfo{-1, -1},
438 FlowGraph::CompilationModeFrom(is_optimizing));
439 }
440
441 CompilerState& state_;
442 FlowGraph& flow_graph_;
443
444 struct PendingPhiInput {
445 PhiInstr* phi;
446 IncomingDef incoming;
447 };
448 GrowableArray<PendingPhiInput> pending_phis_;
449};
450
451} // namespace dart
452
453#endif // RUNTIME_VM_COMPILER_BACKEND_IL_TEST_HELPER_H_
#define EXPECT(type, expectedAlignment, expectedSize)
GLenum type
size_t size() const
Definition: SkString.h:131
void Add(const T &value)
static constexpr bool kCannotBeSentinel
Definition: compile_type.h:49
static constexpr bool kCanBeNull
Definition: compile_type.h:45
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:23418
IncomingDef(BlockEntryInstr *from, T **defn_source)
IncomingDef(BlockEntryInstr *from, Definition *defn)
JoinEntryInstr * JoinEntry(intptr_t try_index=kInvalidTryIndex) const
FlowGraphBuilderHelper(intptr_t num_parameters=0, const std::function< void(const Function &)> &configure_function=[](const Function &) {})
PhiInstr * Phi(JoinEntryInstr *join, std::initializer_list< IncomingDef > incoming)
void AddVariable(const char *name, const AbstractType &static_type, CompileType *inferred_arg_type=nullptr)
TargetEntryInstr * TargetEntry(intptr_t try_index=kInvalidTryIndex) const
ConstantInstr * DoubleConstant(double value)
ConstantInstr * IntConstant(int64_t value, Representation representation=kTagged) const
ConstantInstr * GetConstant(const Object &object, Representation representation=kTagged)
Definition: flow_graph.cc:187
static constexpr CompilationMode CompilationModeFrom(bool is_optimizing)
Definition: flow_graph.h:585
ConstantInstr * constant_dead() const
Definition: flow_graph.h:272
void CreateCommonConstants()
Definition: flow_graph.cc:1143
void DiscoverBlocks()
Definition: flow_graph.cc:346
void ComputeDominators(GrowableArray< BitVector * > *dominance_frontier)
Definition: flow_graph.cc:975
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:11608
static FunctionTypePtr New(intptr_t num_parent_type_arguments=0, Nullability nullability=Nullability::kNonNullable, Heap::Space space=Heap::kOld)
Definition: object.cc:11631
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:10243
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:22999
ObjectStore * object_store() const
Definition: isolate.h:510
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:723
TestPipeline(CompilerPass::PipelineMode mode, std::function< FlowGraph *()> fn)
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
Definition: thread_state.h:37
static Thread * Current()
Definition: thread.h:362
IsolateGroup * isolate_group() const
Definition: thread.h:541
Definition: il.h:75
Dart_NativeFunction(* Dart_NativeEntryResolver)(Dart_Handle name, int num_of_arguments, bool *auto_setup_scope)
Definition: dart_api.h:3234
#define ASSERT(E)
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)
Definition: dart_vm.cc:33
LibraryPtr LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri)
@ kMatchAndMoveBranchFalse
@ kMoveDebugStepChecks
@ kInvalidMatchOpCode
@ kMatchAndMoveBranchTrue
@ kMoveParallelMoves
const char *const name
CompilerTracing
Representation
Definition: locations.h:66
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)
NOT_IN_PRODUCT(LibraryPtr ReloadTestScript(const char *script))
InstructionsPtr BuildInstructions(std::function< void(compiler::Assembler *assembler)> fun)
static constexpr intptr_t kInvalidTryIndex
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
Definition: ref_ptr.h:256
#define T
Definition: precompiler.cc:65
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
static constexpr bool IsUnboxedInteger(Representation rep)
Definition: locations.h:92
#define RESOLVED_USER_TEST_URI
Definition: unit_test.h:317