1// Copyright (c) 2012, 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// Class for intrinsifying functions.
5
6#include "vm/compiler/intrinsifier.h"
7
8#include "vm/compiler/assembler/assembler.h"
9#include "vm/compiler/backend/flow_graph.h"
10#include "vm/compiler/backend/flow_graph_compiler.h"
11#include "vm/compiler/backend/il_printer.h"
12#include "vm/compiler/backend/linearscan.h"
13#include "vm/compiler/frontend/flow_graph_builder.h"
14#include "vm/compiler/jit/compiler.h"
15#include "vm/cpu.h"
16#include "vm/flags.h"
17#include "vm/object.h"
18#include "vm/parser.h"
19#include "vm/symbols.h"
20
21namespace dart {
22
23DEFINE_FLAG(bool, intrinsify, true, "Instrinsify when possible");
24DEFINE_FLAG(bool, trace_intrinsifier, false, "Trace intrinsifier");
25
26namespace compiler {
27
28bool Intrinsifier::CanIntrinsify(const Function& function) {
29 if (FLAG_trace_intrinsifier) {
30 THR_Print("CanIntrinsify %s ->", function.ToQualifiedCString());
31 }
32 if (!FLAG_intrinsify) return false;
33 // TODO(regis): We do not need to explicitly filter generic functions here,
34 // unless there are errors we don't detect at link time. Revisit if necessary.
35 if (function.IsClosureFunction()) {
36 if (FLAG_trace_intrinsifier) {
37 THR_Print("No, closure function.\n");
38 }
39 return false;
40 }
41 // Can occur because of compile-all flag.
42 if (function.is_external()) {
43 if (FLAG_trace_intrinsifier) {
44 THR_Print("No, external function.\n");
45 }
46 return false;
47 }
48 if (!function.is_intrinsic() && !CanIntrinsifyFieldAccessor(function)) {
49 if (FLAG_trace_intrinsifier) {
50 THR_Print("No, not intrinsic function.\n");
51 }
52 return false;
53 }
54 switch (function.recognized_kind()) {
55 case MethodRecognizer::kInt64ArrayGetIndexed:
56 case MethodRecognizer::kInt64ArraySetIndexed:
57 case MethodRecognizer::kUint64ArrayGetIndexed:
58 case MethodRecognizer::kUint64ArraySetIndexed:
59 // TODO(ajcbik): consider 32-bit as well.
60 if (target::kBitsPerWord == 64 &&
61 FlowGraphCompiler::SupportsUnboxedInt64()) {
62 break;
63 }
64 if (FLAG_trace_intrinsifier) {
65 THR_Print("No, 64-bit int intrinsic on 32-bit platform.\n");
66 }
67 return false;
68 default:
69 break;
70 }
71 if (FLAG_trace_intrinsifier) {
72 THR_Print("Yes.\n");
73 }
74 return true;
75}
76
77bool Intrinsifier::CanIntrinsifyFieldAccessor(const Function& function) {
78 const bool is_getter = function.IsImplicitGetterFunction();
79 const bool is_setter = function.IsImplicitSetterFunction();
80 if (!is_getter && !is_setter) return false;
81
82 Field& field = Field::Handle(function.accessor_field());
83 ASSERT(!field.IsNull());
84
85 // The checks further down examine the field and its guard.
86 //
87 // In JIT mode we only intrinsify the field accessor if there is no active
88 // guard, meaning the state transition has reached its final `kDynamicCid`
89 // state (where it stays).
90 //
91 // If we intrinsify, the intrinsified code therefore does not depend on the
92 // field guard and we do not add it to the guarded fields via
93 // [ParsedFunction::AddToGuardedFields].
94 if (Field::ShouldCloneFields()) {
95 field = field.CloneFromOriginal();
96 }
97
98 // We only graph intrinsify implicit instance getters/setter for now.
99 if (!field.is_instance()) return false;
100
101 if (is_getter) {
102 // We don't support complex getter cases.
103 if (field.is_late() || field.needs_load_guard()) return false;
104
105 if (FlowGraphCompiler::IsPotentialUnboxedField(field)) {
106 if (function.HasUnboxedReturnValue()) {
107 // In AOT mode: Unboxed fields contain the unboxed value and can be
108 // returned in unboxed form.
109 ASSERT(FLAG_precompiled_mode);
110 } else {
111 // In JIT mode: Unboxed fields contain a mutable box which we cannot
112 // return.
113 return false;
114 }
115 } else {
116 // If the field is boxed, then the getter cannot return an unboxed value
117 // either (if it could, we would know the field itself can be unboxed).
118 RELEASE_ASSERT(!function.HasUnboxedReturnValue());
119 }
120 } else {
121 ASSERT(is_setter);
122
123 // We don't support complex setter cases.
124 if (field.is_final()) {
125 RELEASE_ASSERT(field.is_late());
126 return false;
127 }
128
129 // We only support cases where there is no need to check for argument types.
130 //
131 // Normally we have to check the parameter type.
132 ASSERT(function.NeedsArgumentTypeChecks());
133 // Dynamic call sites will go to dyn:set:* instead.
134 ASSERT(!function.CanReceiveDynamicInvocation());
135 // Covariant parameter types have to be checked, which we don't support.
136 if (field.is_covariant() || field.is_generic_covariant_impl()) return false;
137
138 // If the incoming value is unboxed we only support real unboxed fields to
139 // avoid the need for boxing (which we cannot do in the intrinsic).
140 if (function.HasUnboxedParameters()) {
141 ASSERT(FLAG_precompiled_mode);
142 if (!FlowGraphCompiler::IsUnboxedField(field)) {
143 return false;
144 }
145 }
146
147 // We don't support field guards in graph intrinsic stores.
148 if (!FLAG_precompiled_mode && field.guarded_cid() != kDynamicCid) {
149 return false;
150 }
151 }
152
153 return true;
154}
155
156struct IntrinsicDesc {
157 const char* class_name;
158 const char* function_name;
159};
160
161struct LibraryInstrinsicsDesc {
162 Library& library;
163 IntrinsicDesc* intrinsics;
164};
165
166#define DEFINE_INTRINSIC(class_name, function_name, destination, fp) \
167 {#class_name, #function_name},
168
169// clang-format off
170static IntrinsicDesc core_intrinsics[] = {
171 CORE_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
172 CORE_INTEGER_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
173 GRAPH_CORE_INTRINSICS_LIST(DEFINE_INTRINSIC)
174 {nullptr, nullptr},
175};
176
177static IntrinsicDesc math_intrinsics[] = {
178 MATH_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
179 GRAPH_MATH_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
180 {nullptr, nullptr},
181};
182
183static IntrinsicDesc typed_data_intrinsics[] = {
184 TYPED_DATA_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
185 GRAPH_TYPED_DATA_INTRINSICS_LIST(DEFINE_INTRINSIC)
186 {nullptr, nullptr},
187};
188
189static IntrinsicDesc developer_intrinsics[] = {
190 DEVELOPER_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
191 {nullptr, nullptr},
192};
193
194static IntrinsicDesc internal_intrinsics[] = {
195 INTERNAL_LIB_INTRINSIC_LIST(DEFINE_INTRINSIC)
196 {nullptr, nullptr},
197};
198// clang-format on
199
200void Intrinsifier::InitializeState() {
201 Thread* thread = Thread::Current();
202 Zone* zone = thread->zone();
203 Library& lib = Library::Handle(zone);
204 Class& cls = Class::Handle(zone);
205 Function& func = Function::Handle(zone);
206 String& str = String::Handle(zone);
207 String& str2 = String::Handle(zone);
208 Error& error = Error::Handle(zone);
209
210 static const intptr_t kNumLibs = 5;
211 LibraryInstrinsicsDesc intrinsics[kNumLibs] = {
212 {Library::Handle(zone, Library::CoreLibrary()), core_intrinsics},
213 {Library::Handle(zone, Library::MathLibrary()), math_intrinsics},
214 {Library::Handle(zone, Library::TypedDataLibrary()),
215 typed_data_intrinsics},
216 {Library::Handle(zone, Library::DeveloperLibrary()),
217 developer_intrinsics},
218 {Library::Handle(zone, Library::InternalLibrary()), internal_intrinsics},
219 };
220
221 for (intptr_t i = 0; i < kNumLibs; i++) {
222 lib = intrinsics[i].library.raw();
223 for (IntrinsicDesc* intrinsic = intrinsics[i].intrinsics;
224 intrinsic->class_name != nullptr; intrinsic++) {
225 func = Function::null();
226 if (strcmp(intrinsic->class_name, "::") == 0) {
227 str = String::New(intrinsic->function_name);
228 func = lib.LookupFunctionAllowPrivate(str);
229 } else {
230 str = String::New(intrinsic->class_name);
231 cls = lib.LookupClassAllowPrivate(str);
232 ASSERT(FLAG_precompiled_mode || !cls.IsNull());
233 if (!cls.IsNull()) {
234 error = cls.EnsureIsFinalized(thread);
235 if (!error.IsNull()) {
236 OS::PrintErr("%s\n", error.ToErrorCString());
237 }
238 ASSERT(error.IsNull());
239 str = String::New(intrinsic->function_name);
240 if (intrinsic->function_name[0] == '.') {
241 str2 = String::New(intrinsic->class_name);
242 str = String::Concat(str2, str);
243 }
244 func = cls.LookupFunctionAllowPrivate(str);
245 }
246 }
247 if (!func.IsNull()) {
248 func.set_is_intrinsic(true);
249 } else if (!FLAG_precompiled_mode) {
250 FATAL2("Intrinsifier failed to find method %s in class %s\n",
251 intrinsic->function_name, intrinsic->class_name);
252 }
253 }
254 }
255#undef SETUP_FUNCTION
256}
257
258// Returns true if fall-through code can be omitted.
259bool Intrinsifier::Intrinsify(const ParsedFunction& parsed_function,
260 FlowGraphCompiler* compiler) {
261 const Function& function = parsed_function.function();
262 if (!CanIntrinsify(function)) {
263 return false;
264 }
265
266 ASSERT(!compiler->flow_graph().IsCompiledForOsr());
267 if (GraphIntrinsifier::GraphIntrinsify(parsed_function, compiler)) {
268 return compiler->intrinsic_slow_path_label()->IsUnused();
269 }
270
271#if !defined(HASH_IN_OBJECT_HEADER)
272 // These two are more complicated on 32 bit platforms, where the
273 // identity hash is not stored in the header of the object. We
274 // therefore don't intrinsify them, falling back on the native C++
275 // implementations.
276 if (function.recognized_kind() == MethodRecognizer::kObject_getHash ||
277 function.recognized_kind() == MethodRecognizer::kObject_setHash) {
278 return false;
279 }
280#endif
281
282#if !defined(PRODUCT)
283#define EMIT_BREAKPOINT() compiler->assembler()->Breakpoint()
284#else
285#define EMIT_BREAKPOINT()
286#endif
287
288#define EMIT_CASE(class_name, function_name, enum_name, fp) \
289 case MethodRecognizer::k##enum_name: { \
290 compiler->assembler()->Comment("Intrinsic"); \
291 Label normal_ir_body; \
292 const auto size_before = compiler->assembler()->CodeSize(); \
293 AsmIntrinsifier::enum_name(compiler->assembler(), &normal_ir_body); \
294 const auto size_after = compiler->assembler()->CodeSize(); \
295 if (size_before == size_after) return false; \
296 if (!normal_ir_body.IsBound()) { \
297 EMIT_BREAKPOINT(); \
298 return true; \
299 } \
300 return false; \
301 }
302
303 switch (function.recognized_kind()) {
304 ALL_INTRINSICS_NO_INTEGER_LIB_LIST(EMIT_CASE);
305 default:
306 break;
307 }
308 switch (function.recognized_kind()) {
309 CORE_INTEGER_LIB_INTRINSIC_LIST(EMIT_CASE)
310 default:
311 break;
312 }
313
314#undef EMIT_BREAKPOINT
315
316#undef EMIT_INTRINSIC
317 return false;
318}
319
320} // namespace compiler
321} // namespace dart
322