Flutter Engine
The Flutter Engine
il_test_helper.cc
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
6
19#include "vm/dart_api_impl.h"
20#include "vm/flags.h"
21#include "vm/parser.h"
22#include "vm/unit_test.h"
23
24namespace dart {
25
26LibraryPtr LoadTestScript(const char* script,
28 const char* lib_uri) {
29 Dart_Handle api_lib;
30 {
32 api_lib = TestCase::LoadTestScript(script, resolver, lib_uri);
33 EXPECT_VALID(api_lib);
34 }
35 auto& lib = Library::Handle();
36 lib ^= Api::UnwrapHandle(api_lib);
37 EXPECT(!lib.IsNull());
38 return lib.ptr();
39}
40
41#if !defined(PRODUCT)
42LibraryPtr ReloadTestScript(const char* script) {
43 Dart_Handle api_lib;
44 {
47 EXPECT_VALID(api_lib);
48 }
49 auto& lib = Library::Handle();
50 lib ^= Api::UnwrapHandle(api_lib);
51 EXPECT(!lib.IsNull());
52 return lib.ptr();
53}
54#endif
55
56FunctionPtr GetFunction(const Library& lib, const char* name) {
57 Thread* thread = Thread::Current();
58 const auto& func = Function::Handle(lib.LookupFunctionAllowPrivate(
60 EXPECT(!func.IsNull());
61 return func.ptr();
62}
63
64ClassPtr GetClass(const Library& lib, const char* name) {
65 Thread* thread = Thread::Current();
66 const auto& cls = Class::Handle(
68 EXPECT(!cls.IsNull());
69 return cls.ptr();
70}
71
72TypeParameterPtr GetClassTypeParameter(const Class& klass, intptr_t index) {
73 const auto& param = TypeParameter::Handle(klass.TypeParameterAt(index));
74 EXPECT(!param.IsNull());
75 return param.ptr();
76}
77
78TypeParameterPtr GetFunctionTypeParameter(const Function& fun, intptr_t index) {
79 const auto& param = TypeParameter::Handle(fun.TypeParameterAt(index));
80 EXPECT(!param.IsNull());
81 return param.ptr();
82}
83
84ObjectPtr Invoke(const Library& lib, const char* name) {
85 Thread* thread = Thread::Current();
86 Dart_Handle api_lib = Api::NewHandle(thread, lib.ptr());
88 {
89 TransitionVMToNative transition(thread);
90 result =
91 Dart_Invoke(api_lib, NewString(name), /*argc=*/0, /*argv=*/nullptr);
93 }
95}
96
97InstructionsPtr BuildInstructions(
98 std::function<void(compiler::Assembler* assembler)> fun) {
99 auto thread = Thread::Current();
100 compiler::Assembler assembler(nullptr);
101
102 fun(&assembler);
103
104 auto& code = Code::Handle();
105 auto install_code_fun = [&] {
106 code = Code::FinalizeCode(nullptr, &assembler,
108 /*optimized=*/false, /*stats=*/nullptr);
109 };
110 SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock());
111 thread->isolate_group()->RunWithStoppedMutators(install_code_fun,
112 /*use_force_growth=*/true);
113 return code.instructions();
114}
115
117 std::initializer_list<CompilerPass::Id> passes) {
118 auto thread = Thread::Current();
119 auto zone = thread->zone();
120 const bool optimized = true;
121 const intptr_t osr_id = Compiler::kNoOSRDeoptId;
122
123 // We assume that prebuilt graph is already in SSA form so we should
124 // avoid running ComputeSSA on it (it will just crash).
125 const bool is_ssa = (flow_graph_ != nullptr);
126 if (flow_graph_ == nullptr) {
127 auto pipeline = CompilationPipeline::New(zone, function_);
128
129 parsed_function_ = new (zone)
130 ParsedFunction(thread, Function::ZoneHandle(zone, function_.ptr()));
131 pipeline->ParseFunction(parsed_function_);
132
133 // Extract type feedback before the graph is built, as the graph
134 // builder uses it to attach it to nodes.
135 ic_data_array_ = new (zone) ZoneGrowableArray<const ICData*>();
136 if (mode_ == CompilerPass::kJIT) {
137 function_.RestoreICDataMap(ic_data_array_, /*clone_ic_data=*/false);
138 }
139
140 flow_graph_ = pipeline->BuildFlowGraph(zone, parsed_function_,
141 ic_data_array_, osr_id, optimized);
142 }
143
144 if (mode_ == CompilerPass::kAOT) {
145 flow_graph_->PopulateWithICData(function_);
146 }
147
148 if (mode_ == CompilerPass::kJIT && flow_graph_->should_reorder_blocks()) {
150 }
151
152 pass_state_ =
153 new CompilerPassState(thread, flow_graph_, speculative_policy_.get());
154
155 if (optimized) {
156 JitCallSpecializer jit_call_specializer(flow_graph_,
157 speculative_policy_.get());
158 AotCallSpecializer aot_call_specializer(
159 /*precompiler=*/nullptr, flow_graph_, speculative_policy_.get());
160 if (mode_ == CompilerPass::kAOT) {
161 pass_state_->call_specializer = &aot_call_specializer;
162 } else {
163 pass_state_->call_specializer = &jit_call_specializer;
164 }
165
166 if (passes.size() > 0) {
167 flow_graph_ = CompilerPass::RunPipelineWithPasses(pass_state_, passes);
168 } else {
169 flow_graph_ = CompilerPass::RunPipeline(mode_, pass_state_,
170 /*compute_ssa=*/!is_ssa);
171 }
172 pass_state_->call_specializer = nullptr;
173 }
174
175 return flow_graph_;
176}
177
179 std::initializer_list<CompilerPass::Id> passes) {
180 JitCallSpecializer jit_call_specializer(flow_graph_,
181 speculative_policy_.get());
182 AotCallSpecializer aot_call_specializer(/*precompiler=*/nullptr, flow_graph_,
183 speculative_policy_.get());
184 if (mode_ == CompilerPass::kAOT) {
185 pass_state_->call_specializer = &aot_call_specializer;
186 } else {
187 pass_state_->call_specializer = &jit_call_specializer;
188 }
189
190 flow_graph_ = CompilerPass::RunPipelineWithPasses(pass_state_, passes);
191 pass_state_->call_specializer = nullptr;
192}
193
196 CompilerPass::kSetOuterInliningId,
197 CompilerPass::kTypePropagation,
198 CompilerPass::kCanonicalize,
199 CompilerPass::kBranchSimplify,
200 CompilerPass::kIfConvert,
201 CompilerPass::kConstantPropagation,
202 CompilerPass::kTypePropagation,
203 CompilerPass::kSelectRepresentations_Final,
204 CompilerPass::kTypePropagation,
205 CompilerPass::kTryCatchOptimization,
206 CompilerPass::kEliminateEnvironments,
207 CompilerPass::kEliminateDeadPhis,
208 CompilerPass::kDCE,
209 CompilerPass::kCanonicalize,
210 CompilerPass::kDelayAllocations,
211 CompilerPass::kEliminateWriteBarriers,
212 CompilerPass::kFinalizeGraph,
213 CompilerPass::kAllocateRegisters,
214 CompilerPass::kReorderBlocks,
215 });
216}
217
219 Zone* zone = thread_->zone();
220 const bool optimized = true;
221
222 SpeculativeInliningPolicy speculative_policy(/*enable_suppression=*/false);
223
224#if defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_IA32)
225 const intptr_t far_branch_level = 0;
226#else
227 const intptr_t far_branch_level = 1;
228#endif
229
230 ASSERT(pass_state_->inline_id_to_function.length() ==
231 pass_state_->caller_inline_id.length());
232 compiler::ObjectPoolBuilder object_pool_builder;
233 compiler::Assembler assembler(&object_pool_builder, far_branch_level);
234 FlowGraphCompiler graph_compiler(
235 &assembler, flow_graph_, *parsed_function_, optimized,
236 &speculative_policy, pass_state_->inline_id_to_function,
237 pass_state_->inline_id_to_token_pos, pass_state_->caller_inline_id,
238 ic_data_array_);
239
240 graph_compiler.CompileGraph();
241
242 const auto& deopt_info_array =
243 Array::Handle(zone, graph_compiler.CreateDeoptInfo(&assembler));
244 const auto pool_attachment = Code::PoolAttachment::kAttachPool;
246 {
247 SafepointWriteRwLocker ml(thread_,
248 thread_->isolate_group()->program_lock());
249 code ^= Code::FinalizeCode(&graph_compiler, &assembler, pool_attachment,
250 optimized, nullptr);
251 }
252 code.set_is_optimized(optimized);
253 code.set_owner(function_);
254
255 graph_compiler.FinalizePcDescriptors(code);
256 code.set_deopt_info_array(deopt_info_array);
257
258 graph_compiler.FinalizeStackMaps(code);
259 graph_compiler.FinalizeVarDescriptors(code);
260 graph_compiler.FinalizeExceptionHandlers(code);
261 graph_compiler.FinalizeCatchEntryMovesMap(code);
262 graph_compiler.FinalizeStaticCallTargetsTable(code);
263 graph_compiler.FinalizeCodeSourceMap(code);
264
265 {
266 SafepointWriteRwLocker ml(thread_,
267 thread_->isolate_group()->program_lock());
268 if (optimized) {
269 function_.InstallOptimizedCode(code);
270 } else {
271 function_.set_unoptimized_code(code);
272 function_.AttachCode(code);
273 }
274 }
275
276 // We expect there to be no deoptimizations.
277 if (mode_ == CompilerPass::kAOT) {
278 EXPECT(deopt_info_array.IsNull() || deopt_info_array.Length() == 0);
279 }
280
281#if !defined(PRODUCT)
282 if (FLAG_disassemble_optimized) {
283 Disassembler::DisassembleCode(function_, code, optimized);
284 }
285#endif
286}
287
288bool ILMatcher::TryMatch(std::initializer_list<MatchCode> match_codes,
289 MatchOpCode insert_before) {
290 std::vector<MatchCode> qcodes = match_codes;
291
292 if (insert_before != kInvalidMatchOpCode) {
293 for (auto pos = qcodes.begin(); pos < qcodes.end(); pos++) {
294 pos = qcodes.insert(pos, insert_before) + 1;
295 }
296 }
297
298 if (trace_) {
299 OS::PrintErr("ILMatcher: Matching the following graph\n");
300 FlowGraphPrinter::PrintGraph("ILMatcher", flow_graph_);
301 OS::PrintErr("ILMatcher: Starting match at %s:\n", cursor_->ToCString());
302 }
303
304 Instruction* cursor = cursor_;
305 for (size_t i = 0; i < qcodes.size(); ++i) {
306 Instruction** capture = qcodes[i].capture_;
307 if (parallel_moves_handling_ == ParallelMovesHandling::kSkip) {
308 while (cursor->IsParallelMove()) {
309 cursor = cursor->next();
310 }
311 }
312 if (trace_) {
313 OS::PrintErr(" matching %30s @ %s\n",
314 MatchOpCodeToCString(qcodes[i].opcode()),
315 cursor->ToCString());
316 }
317
318 auto next = MatchInternal(qcodes, i, cursor);
319 if (next == nullptr) {
320 if (trace_) {
321 OS::PrintErr(" -> Match failed\n");
322 }
323 cursor = next;
324 break;
325 }
326 if (capture != nullptr) {
327 *capture = cursor;
328 }
329 cursor = next;
330 }
331 if (cursor != nullptr) {
332 cursor_ = cursor;
333 return true;
334 }
335 return false;
336}
337
338Instruction* ILMatcher::MatchInternal(std::vector<MatchCode> match_codes,
339 size_t i,
340 Instruction* cursor) {
341 const MatchOpCode opcode = match_codes[i].opcode();
342 if (opcode == kMatchAndMoveBranchTrue) {
343 auto branch = cursor->AsBranch();
344 if (branch == nullptr) return nullptr;
345 return branch->true_successor();
346 }
347 if (opcode == kMatchAndMoveBranchFalse) {
348 auto branch = cursor->AsBranch();
349 if (branch == nullptr) return nullptr;
350 return branch->false_successor();
351 }
352 if (opcode == kNop) {
353 return cursor;
354 }
355 if (opcode == kMoveAny) {
356 return cursor->next();
357 }
358 if (opcode == kMoveParallelMoves) {
359 while (cursor != nullptr && cursor->IsParallelMove()) {
360 cursor = cursor->next();
361 }
362 return cursor;
363 }
364
365 if (opcode == kMoveGlob) {
366 ASSERT((i + 1) < match_codes.size());
367 while (true) {
368 if (cursor == nullptr) return nullptr;
369 if (MatchInternal(match_codes, i + 1, cursor) != nullptr) {
370 return cursor;
371 }
372 if (auto as_goto = cursor->AsGoto()) {
373 cursor = as_goto->successor();
374 } else {
375 cursor = cursor->next();
376 }
377 }
378 }
379
380 if (opcode == kMoveDebugStepChecks) {
381 while (cursor != nullptr && cursor->IsDebugStepCheck()) {
382 cursor = cursor->next();
383 }
384 return cursor;
385 }
386
387 if (opcode == kMatchAndMoveGoto) {
388 if (auto goto_instr = cursor->AsGoto()) {
389 return goto_instr->successor();
390 }
391 }
392
393 switch (opcode) {
394#define EMIT_CASE(Instruction, _) \
395 case kMatch##Instruction: { \
396 if (cursor->Is##Instruction()) { \
397 return cursor; \
398 } \
399 return nullptr; \
400 } \
401 case kMatchAndMove##Instruction: { \
402 if (cursor->Is##Instruction()) { \
403 return cursor->next(); \
404 } \
405 return nullptr; \
406 } \
407 case kMatchAndMoveOptional##Instruction: { \
408 if (cursor->Is##Instruction()) { \
409 return cursor->next(); \
410 } \
411 return cursor; \
412 }
415#undef EMIT_CASE
416 default:
417 UNREACHABLE();
418 }
419
420 UNREACHABLE();
421 return nullptr;
422}
423
424const char* ILMatcher::MatchOpCodeToCString(MatchOpCode opcode) {
425 if (opcode == kMatchAndMoveBranchTrue) {
426 return "kMatchAndMoveBranchTrue";
427 }
428 if (opcode == kMatchAndMoveBranchFalse) {
429 return "kMatchAndMoveBranchFalse";
430 }
431 if (opcode == kNop) {
432 return "kNop";
433 }
434 if (opcode == kMoveAny) {
435 return "kMoveAny";
436 }
437 if (opcode == kMoveParallelMoves) {
438 return "kMoveParallelMoves";
439 }
440 if (opcode == kMoveGlob) {
441 return "kMoveGlob";
442 }
443 if (opcode == kMoveDebugStepChecks) {
444 return "kMoveDebugStepChecks";
445 }
446
447 switch (opcode) {
448#define EMIT_CASE(Instruction, _) \
449 case kMatch##Instruction: \
450 return "kMatch" #Instruction; \
451 case kMatchAndMove##Instruction: \
452 return "kMatchAndMove" #Instruction; \
453 case kMatchAndMoveOptional##Instruction: \
454 return "kMatchAndMoveOptional" #Instruction;
457#undef EMIT_CASE
458 default:
459 UNREACHABLE();
460 }
461
462 UNREACHABLE();
463 return nullptr;
464}
465
466} // namespace dart
SkPoint pos
static float next(float f)
#define EXPECT(type, expectedAlignment, expectedSize)
#define UNREACHABLE()
Definition: assert.h:248
static Dart_Handle NewHandle(Thread *thread, ObjectPtr raw)
static ObjectPtr UnwrapHandle(Dart_Handle object)
intptr_t length() const
static void AssignEdgeWeights(FlowGraph *flow_graph)
TypeParameterPtr TypeParameterAt(intptr_t index, Nullability nullability=Nullability::kNonNullable) const
Definition: object.cc:3689
static CodePtr FinalizeCode(FlowGraphCompiler *compiler, compiler::Assembler *assembler, PoolAttachment pool_attachment, bool optimized, CodeStatistics *stats)
Definition: object.cc:18018
static CompilationPipeline * New(Zone *zone, const Function &function)
Definition: compiler.cc:201
static DART_WARN_UNUSED_RESULT FlowGraph * RunPipeline(PipelineMode mode, CompilerPassState *state, bool compute_ssa=true)
static DART_WARN_UNUSED_RESULT FlowGraph * RunPipelineWithPasses(CompilerPassState *state, std::initializer_list< CompilerPass::Id > passes)
static constexpr intptr_t kNoOSRDeoptId
Definition: compiler.h:73
static void DisassembleCode(const Function &function, const Code &code, bool optimized)
void FinalizeVarDescriptors(const Code &code)
void FinalizeCatchEntryMovesMap(const Code &code)
void FinalizeStaticCallTargetsTable(const Code &code)
void FinalizeExceptionHandlers(const Code &code)
void FinalizeStackMaps(const Code &code)
void FinalizeCodeSourceMap(const Code &code)
ArrayPtr CreateDeoptInfo(compiler::Assembler *assembler)
void FinalizePcDescriptors(const Code &code)
static void PrintGraph(const char *phase, FlowGraph *flow_graph)
Definition: il_printer.cc:1706
void PopulateWithICData(const Function &function)
Definition: flow_graph.cc:2528
bool should_reorder_blocks() const
Definition: flow_graph.h:508
void set_unoptimized_code(const Code &value) const
Definition: object.cc:8038
void RestoreICDataMap(ZoneGrowableArray< const ICData * > *deopt_id_to_ic_data, bool clone_ic_data) const
Definition: object.cc:11217
void InstallOptimizedCode(const Code &code) const
Definition: object.cc:7899
void AttachCode(const Code &value) const
Definition: object.cc:7927
TypeParameterPtr TypeParameterAt(intptr_t index, Nullability nullability=Nullability::kNonNullable) const
Definition: object.cc:8881
bool TryMatch(std::initializer_list< MatchCode > match_codes, MatchOpCode insert_before=kInvalidMatchOpCode)
Instruction * next() const
Definition: il.h:1093
const char * ToCString() const
Definition: il_printer.cc:1683
SafepointRwLock * program_lock()
Definition: isolate.h:537
ClassPtr LookupClassAllowPrivate(const String &name) const
Definition: object.cc:14113
FunctionPtr LookupFunctionAllowPrivate(const String &name) const
Definition: object.cc:14084
static void static void PrintErr(const char *format,...) PRINTF_ATTRIBUTE(1
ObjectPtr ptr() const
Definition: object.h:332
static Object & Handle()
Definition: object.h:407
static Object & ZoneHandle()
Definition: object.h:419
static StringPtr New(Thread *thread, const char *cstr)
Definition: symbols.h:723
static Dart_Handle LoadTestScript(const char *script, Dart_NativeEntryResolver resolver, const char *lib_uri=RESOLVED_USER_TEST_URI, bool finalize=true, bool allow_compile_errors=false)
Definition: unit_test.cc:436
static Dart_Handle ReloadTestScript(const char *script)
Definition: unit_test.cc:613
void RunAdditionalPasses(std::initializer_list< CompilerPass::Id > passes)
FlowGraph * RunPasses(std::initializer_list< CompilerPass::Id > passes)
void CompileGraphAndAttachFunction()
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
struct _Dart_Handle * Dart_Handle
Definition: dart_api.h:258
Dart_NativeFunction(* Dart_NativeEntryResolver)(Dart_Handle name, int num_of_arguments, bool *auto_setup_scope)
Definition: dart_api.h:3234
#define ASSERT(E)
GAsyncResult * result
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 EMIT_CASE(Instruction, _)
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
DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target, Dart_Handle name, int number_of_arguments, Dart_Handle *arguments)
ObjectPtr Invoke(const Library &lib, const char *name)
FunctionPtr GetFunction(const Library &lib, const char *name)
LibraryPtr ReloadTestScript(const char *script)
ClassPtr GetClass(const Library &lib, const char *name)
Dart_Handle NewString(const char *str)
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)
CallSpecializer * call_specializer
Definition: compiler_pass.h:94
GrowableArray< TokenPosition > inline_id_to_token_pos
Definition: compiler_pass.h:90
GrowableArray< intptr_t > caller_inline_id
Definition: compiler_pass.h:92
GrowableArray< const Function * > inline_id_to_function
Definition: compiler_pass.h:88
#define EXPECT_VALID(handle)
Definition: unit_test.h:643