Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
dispatch_table_generator.cc
Go to the documentation of this file.
1// Copyright (c) 2020, 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#if defined(DART_PRECOMPILER)
6
8
9#include <memory>
10
12#include "vm/dispatch_table.h"
13#include "vm/stub_code.h"
14#include "vm/thread.h"
15
16#define Z zone_
17
18namespace dart {
19namespace compiler {
20
21class Interval {
22 public:
23 Interval() : begin_(-1), end_(-1) {}
24 Interval(int32_t begin, int32_t end) : begin_(begin), end_(end) {
25 ASSERT(end > begin);
26 }
27
28 int32_t begin() const { return begin_; }
29 void set_begin(int32_t value) { begin_ = value; }
30
31 int32_t end() const { return end_; }
32 void set_end(int32_t value) { end_ = value; }
33
34 int32_t length() const { return end_ - begin_; }
35
36 Interval WithOffset(int32_t offset) const {
37 return Interval(begin_ + offset, end_ + offset);
38 }
39
40 bool IsSame(const Interval other) const {
41 return end() == other.end() && begin() == other.begin();
42 }
43
44 bool IsBefore(const Interval other) const { return end() <= other.begin(); }
45
46 bool IsAfter(const Interval other) const { return begin() >= other.end(); }
47
48 bool Overlap(const Interval other) const {
49 return !IsBefore(other) && !IsAfter(other);
50 }
51
52 bool ContainsBeginOf(const Interval other) const {
53 return begin() <= other.begin() && other.begin() <= end();
54 }
55
56 bool ContainsEndOf(const Interval other) const {
57 return begin() <= other.end() && other.end() <= end();
58 }
59
60 bool Contains(const Interval other) const {
61 return ContainsBeginOf(other) && ContainsEndOf(other);
62 }
63
64 void ExtendToIncludeInterval(const Interval& other) {
65 if (other.begin() < begin_) begin_ = other.begin();
66 if (other.end() > end_) end_ = other.end();
67 }
68
69 private:
70 int32_t begin_;
71 int32_t end_;
72};
73
74class CidInterval {
75 public:
76 CidInterval(classid_t cid,
77 int16_t depth,
78 Interval range,
79 const Function* function)
80 : cid_(cid), depth_(depth), range_(range), function_(function) {}
81
82 classid_t cid() const { return cid_; }
83 int16_t depth() const { return depth_; }
84 const Interval& range() const { return range_; }
85 Interval& range() { return range_; }
86 const Function* function() const { return function_; }
87
88 private:
89 classid_t cid_;
90 int16_t depth_;
91 Interval range_;
92 const Function* function_;
93};
94
95class SelectorRow {
96 public:
97 SelectorRow(Zone* zone, TableSelector* selector)
98 : selector_(selector),
99 class_ranges_(zone, 0),
100 ranges_(zone, 0),
101 code_(Code::ZoneHandle(zone)) {}
102
103 TableSelector* selector() const { return selector_; }
104
105 int32_t total_size() const { return total_size_; }
106
107 const GrowableArray<Interval>& ranges() const { return ranges_; }
108
109 const GrowableArray<CidInterval>& class_ranges() const {
110 return class_ranges_;
111 }
112
113 void DefineSelectorImplementationForInterval(classid_t cid,
114 int16_t depth,
115 const Interval& range,
116 const Function* function);
117 bool Finalize();
118
119 int32_t CallCount() const { return selector_->call_count; }
120
121 bool IsAllocated() const {
122 return selector_->offset != SelectorMap::kInvalidSelectorOffset;
123 }
124
125 void AllocateAt(int32_t offset) {
126 ASSERT(!IsAllocated());
127 selector_->offset = offset;
128 }
129
130 void FillTable(ClassTable* class_table, const Array& entries);
131
132 private:
133 TableSelector* selector_;
134 int32_t total_size_ = 0;
135
136 GrowableArray<CidInterval> class_ranges_;
137 GrowableArray<Interval> ranges_;
138 Code& code_;
139};
140
141class RowFitter {
142 public:
143 RowFitter() : first_slot_index_(0) { free_slots_.Add(Interval(0, INT_MAX)); }
144
145 // Try to fit a row at the specified offset and return whether it was
146 // successful. If successful, the entries taken up by the row are marked
147 // internally as occupied. If unsuccessful, next_offset is set to the next
148 // potential offset where the row might fit.
149 bool TryFit(SelectorRow* row, int32_t offset, int32_t* next_offset);
150
151 // If the row is not already allocated, try to fit it within the given range
152 // of offsets and allocate it if successful.
153 void FitAndAllocate(SelectorRow* row,
154 int32_t min_offset,
155 int32_t max_offset = INT32_MAX);
156
157 int32_t TableSize() const { return free_slots_.Last().begin(); }
158
159 private:
160 intptr_t MoveForwardToCover(const Interval range, intptr_t slot_index);
161
162 void UpdateFreeSlots(int32_t offset,
163 const GrowableArray<Interval>& ranges,
164 intptr_t slot_index);
165
166 intptr_t FitInFreeSlot(const Interval range, intptr_t slot_index);
167
168 GrowableArray<Interval> free_slots_;
169 intptr_t first_slot_index_;
170};
171
172void SelectorRow::DefineSelectorImplementationForInterval(
174 int16_t depth,
175 const Interval& range,
176 const Function* function) {
177 CidInterval cid_range(cid, depth, range, function);
178 class_ranges_.Add(cid_range);
179}
180
181bool SelectorRow::Finalize() {
182 if (class_ranges_.length() == 0) {
183 return false;
184 }
185
186 // Make a list of [begin, end) ranges which are disjunct and cover all
187 // areas that [class_ranges_] cover (i.e. there can be holes, but no overlap).
188 for (intptr_t i = 0; i < class_ranges_.length(); i++) {
189 ranges_.Add(class_ranges_[i].range());
190 }
191
192 struct IntervalSorter {
193 static int Compare(const Interval* a, const Interval* b) {
194 if (a->begin() != b->begin()) {
195 return a->begin() - b->begin();
196 }
197 return b->length() - a->length();
198 }
199 };
200
201 ranges_.Sort(IntervalSorter::Compare);
202
203 intptr_t current_index = 0;
204 intptr_t write_index = 1;
205 intptr_t read_index = 1;
206 for (; read_index < ranges_.length(); read_index++) {
207 Interval& current_range = ranges_[current_index];
208 Interval& next_range = ranges_[read_index];
209 if (current_range.Contains(next_range)) {
210 // We drop the entry.
211 } else if (current_range.end() == next_range.begin()) {
212 // We extend the current entry and drop the entry.
213 current_range.ExtendToIncludeInterval(next_range);
214 } else {
215 // We keep the entry.
216 if (read_index != write_index) {
217 ranges_[write_index] = ranges_[read_index];
218 }
219 current_index = write_index;
220 write_index++;
221 }
222 }
223 ranges_.TruncateTo(write_index);
224
225 for (intptr_t i = 0; i < ranges_.length() - 1; i++) {
226 const Interval& a = ranges_[i];
227 const Interval& b = ranges_[i + 1];
228 ASSERT(a.begin() < b.begin());
229 ASSERT(a.end() < b.begin());
230 }
231
232 for (intptr_t i = 0; i < ranges_.length(); i++) {
233 total_size_ += ranges_[i].length();
234 }
235
236 return true;
237}
238
239void SelectorRow::FillTable(ClassTable* class_table, const Array& entries) {
240 // Define the entries in the table by going top-down, which means more
241 // specific ones will override more general ones.
242
243 // Sort by depth.
244 struct IntervalSorter {
245 static int Compare(const CidInterval* a, const CidInterval* b) {
246 ASSERT(a == b || a->depth() != b->depth() ||
247 !a->range().Overlap(b->range()));
248 return a->depth() - b->depth();
249 }
250 };
251 class_ranges_.Sort(IntervalSorter::Compare);
252
253 for (intptr_t i = 0; i < class_ranges_.length(); i++) {
254 const CidInterval& cid_range = class_ranges_[i];
255 const Interval& range = cid_range.range();
256 const Function* function = cid_range.function();
257 if (function != nullptr && function->HasCode()) {
258 code_ = function->CurrentCode();
259 for (classid_t cid = range.begin(); cid < range.end(); cid++) {
260 entries.SetAt(selector()->offset + cid, code_);
261 }
262 }
263 }
264}
265
266void RowFitter::FitAndAllocate(SelectorRow* row,
267 int32_t min_offset,
268 int32_t max_offset) {
269 if (row->IsAllocated()) {
270 return;
271 }
272
273 int32_t next_offset;
274
275 int32_t offset = min_offset;
276 while (offset <= max_offset && !TryFit(row, offset, &next_offset)) {
277 offset = next_offset;
278 }
279 if (offset <= max_offset) {
280 row->AllocateAt(offset);
281 }
282}
283
284bool RowFitter::TryFit(SelectorRow* row, int32_t offset, int32_t* next_offset) {
285 const GrowableArray<Interval>& ranges = row->ranges();
286
287 Interval first_range = ranges[0].WithOffset(offset);
288 if (first_slot_index_ > 0 &&
289 free_slots_[first_slot_index_ - 1].end() >= first_range.end()) {
290 // Trying lower offset than last time. Start over in free slots.
291 first_slot_index_ = 0;
292 }
293 first_slot_index_ = MoveForwardToCover(first_range, first_slot_index_);
294 intptr_t slot_index = first_slot_index_;
295
296 for (intptr_t index = 0; index < ranges.length(); index++) {
297 Interval range = ranges[index].WithOffset(offset);
298 slot_index = MoveForwardToCover(range, slot_index);
299 ASSERT(slot_index < free_slots_.length());
300 const Interval slot = free_slots_[slot_index];
301 ASSERT(slot.end() >= range.end());
302 if (slot.begin() > range.begin()) {
303 *next_offset = offset + slot.begin() - range.begin();
304 return false;
305 }
306 }
307
308 UpdateFreeSlots(offset, ranges, first_slot_index_);
309 return true;
310}
311
312intptr_t RowFitter::MoveForwardToCover(const Interval range,
313 intptr_t slot_index) {
314 while (free_slots_[slot_index].end() < range.end()) {
315 slot_index++;
316 }
317 return slot_index;
318}
319
320void RowFitter::UpdateFreeSlots(int32_t offset,
321 const GrowableArray<Interval>& ranges,
322 intptr_t slot_index) {
323 for (intptr_t i = 0; i < ranges.length(); i++) {
324 ASSERT(slot_index < free_slots_.length());
325 const Interval range = ranges[i].WithOffset(offset);
326
327 ASSERT(!free_slots_[slot_index].IsAfter(range));
328 slot_index = MoveForwardToCover(range, slot_index);
329
330 // Assert that we have a valid slot.
331 ASSERT(slot_index < free_slots_.length());
332 ASSERT(free_slots_[slot_index].Contains(range));
333
334 slot_index = FitInFreeSlot(range, slot_index);
335 }
336
337 for (intptr_t i = 0; i < free_slots_.length(); i++) {
338 ASSERT(free_slots_[i].begin() < free_slots_[i].end());
339 }
340}
341
342intptr_t RowFitter::FitInFreeSlot(const Interval range, intptr_t slot_index) {
343 const Interval& slot = free_slots_[slot_index];
344 ASSERT(slot.Contains(range));
345 if (slot.begin() < range.begin()) {
346 Interval free_before = Interval(slot.begin(), range.begin());
347 if (slot.end() > range.end()) {
348 Interval free_after(range.end(), slot.end());
349 free_slots_[slot_index] = free_before;
350 free_slots_.InsertAt(slot_index + 1, free_after);
351 } else {
352 free_slots_[slot_index] = free_before;
353 slot_index++;
354 }
355 } else if (slot.end() <= range.end()) {
356 ASSERT(slot.IsSame(range));
357 free_slots_.EraseAt(slot_index);
358 } else {
359 Interval free_after(range.end(), slot.end());
360 free_slots_[slot_index] = free_after;
361 }
362 return slot_index;
363}
364
365int32_t SelectorMap::SelectorId(const Function& interface_target) const {
366 kernel::ProcedureAttributesMetadata metadata;
367 metadata = kernel::ProcedureAttributesOf(interface_target, Z);
368 return interface_target.IsGetterFunction() ||
369 interface_target.IsImplicitGetterFunction() ||
370 interface_target.IsMethodExtractor()
371 ? metadata.getter_selector_id
372 : metadata.method_or_setter_selector_id;
373}
374
375const TableSelector* SelectorMap::GetSelector(
376 const Function& interface_target) const {
377 const int32_t sid = SelectorId(interface_target);
378 return GetSelector(sid);
379}
380
381const TableSelector* SelectorMap::GetSelector(int32_t sid) const {
382 if (sid == kInvalidSelectorId) return nullptr;
383 const TableSelector* selector = &selectors_[sid];
384 if (!selector->IsUsed()) return nullptr;
385 if (selector->offset == kInvalidSelectorOffset) return nullptr;
386 return selector;
387}
388
389void SelectorMap::AddSelector(int32_t call_count,
390 bool called_on_null,
391 bool torn_off) {
392 const int32_t added_sid = selectors_.length();
393 selectors_.Add(TableSelector(added_sid, call_count, kInvalidSelectorOffset,
394 called_on_null, torn_off));
395}
396
397void SelectorMap::SetSelectorProperties(int32_t sid,
398 bool on_null_interface,
399 bool requires_args_descriptor) {
400 ASSERT(sid < selectors_.length());
401 selectors_[sid].on_null_interface |= on_null_interface;
402 selectors_[sid].requires_args_descriptor |= requires_args_descriptor;
403}
404
406 : zone_(zone),
407 classes_(nullptr),
408 num_selectors_(-1),
409 num_classes_(-1),
410 selector_map_(zone) {}
411
412void DispatchTableGenerator::Initialize(ClassTable* table) {
413 classes_ = table;
414
415 HANDLESCOPE(Thread::Current());
416 ReadTableSelectorInfo();
417 NumberSelectors();
418 SetupSelectorRows();
419 ComputeSelectorOffsets();
420}
421
422void DispatchTableGenerator::ReadTableSelectorInfo() {
423 const auto& object_class = Class::Handle(Z, classes_->At(kInstanceCid));
424 const auto& info =
425 KernelProgramInfo::Handle(Z, object_class.KernelProgramInfo());
426 kernel::TableSelectorMetadata* metadata =
427 kernel::TableSelectorMetadataForProgram(info, Z);
428 // Errors out if gen_kernel was run in non-AOT mode or without TFA.
429 if (metadata == nullptr) {
430 FATAL(
431 "Missing table selector metadata!\n"
432 "Probably gen_kernel was run in non-AOT mode or without TFA.\n");
433 }
434 for (intptr_t i = 0; i < metadata->selectors.length(); i++) {
435 const kernel::TableSelectorInfo* info = &metadata->selectors[i];
436 selector_map_.AddSelector(info->call_count, info->called_on_null,
437 info->torn_off);
438 }
439}
440
441void DispatchTableGenerator::NumberSelectors() {
442 num_classes_ = classes_->NumCids();
443
444 Object& obj = Object::Handle(Z);
445 Class& klass = Class::Handle(Z);
446 Array& functions = Array::Handle(Z);
447 Function& function = Function::Handle(Z);
448
449 for (classid_t cid = kIllegalCid + 1; cid < num_classes_; cid++) {
450 obj = classes_->At(cid);
451 if (obj.IsClass()) {
452 klass = Class::RawCast(obj.ptr());
453 functions = klass.current_functions();
454 if (!functions.IsNull()) {
455 for (intptr_t j = 0; j < functions.Length(); j++) {
456 function ^= functions.At(j);
457 if (function.IsDynamicFunction(/*allow_abstract=*/false)) {
458 const bool on_null_interface = klass.IsObjectClass();
459 const bool requires_args_descriptor =
460 function.PrologueNeedsArgumentsDescriptor();
461 // Get assigned selector ID for this function.
462 const int32_t sid = selector_map_.SelectorId(function);
463 if (sid == SelectorMap::kInvalidSelectorId) {
464 // Probably gen_kernel was run in non-AOT mode or without TFA.
465 FATAL("Function has no assigned selector ID.\n");
466 }
467 selector_map_.SetSelectorProperties(sid, on_null_interface,
468 requires_args_descriptor);
469 }
470 }
471 }
472 }
473 }
474
475 num_selectors_ = selector_map_.NumIds();
476}
477
478void DispatchTableGenerator::SetupSelectorRows() {
479 Object& obj = Object::Handle(Z);
480 Class& klass = Class::Handle(Z);
481 Array& functions = Array::Handle(Z);
482 Function& function = Function::Handle(Z);
483
484 // For each class, we first need to figure out the ranges of cids that will
485 // inherit methods from it (this is due to the fact that cids don't have the
486 // property that they are assigned preorder and don't have holes).
487
488 // Make a condensed array which stores parent cids.
489 std::unique_ptr<classid_t[]> parent_cids(new classid_t[num_classes_]);
490 std::unique_ptr<bool[]> is_concrete_class(new bool[num_classes_]);
491 for (classid_t cid = kIllegalCid + 1; cid < num_classes_; cid++) {
492 classid_t parent_cid = kIllegalCid;
493 bool concrete = false;
494 if (cid > kIllegalCid) {
495 obj = classes_->At(cid);
496 if (obj.IsClass()) {
497 klass = Class::RawCast(obj.ptr());
498 concrete = !klass.is_abstract();
499 klass = klass.SuperClass();
500 if (!klass.IsNull()) {
501 parent_cid = klass.id();
502 }
503 }
504 }
505 parent_cids[cid] = parent_cid;
506 is_concrete_class[cid] = concrete;
507 }
508
509 // Precompute depth level.
510 std::unique_ptr<int16_t[]> cid_depth(new int16_t[num_classes_]);
511 for (classid_t cid = kIllegalCid + 1; cid < num_classes_; cid++) {
512 int16_t depth = 0;
513 classid_t pcid = cid;
514 while (pcid != kIllegalCid) {
515 pcid = parent_cids[pcid];
516 depth++;
517 }
518 cid_depth[cid] = depth;
519 }
520
521 // Find all regions that have [cid] as parent (which should include [cid])!
522 std::unique_ptr<GrowableArray<Interval>[]> cid_subclass_ranges(
523 new GrowableArray<Interval>[num_classes_]());
524 for (classid_t cid = kIllegalCid + 1; cid < num_classes_; cid++) {
526 for (classid_t sub_cid = kIllegalCid + 1; sub_cid < num_classes_;
527 sub_cid++) {
528 // Is [sub_cid] a subclass of [cid]?
529 classid_t pcid = sub_cid;
530 while (pcid != kIllegalCid && pcid != cid) {
531 pcid = parent_cids[pcid];
532 }
533 const bool is_subclass = cid == pcid;
534 const bool in_range = is_subclass && is_concrete_class[sub_cid];
535
536 if (start == kIllegalCid && in_range) {
537 start = sub_cid;
538 } else if (start != kIllegalCid && !in_range) {
539 Interval range(start, sub_cid);
540 cid_subclass_ranges[cid].Add(range);
542 }
543 }
544 if (start != kIllegalCid) {
545 Interval range(start, num_classes_);
546 cid_subclass_ranges[cid].Add(range);
547 }
548 }
549
550 // Initialize selector rows.
551 SelectorRow* selector_rows = Z->Alloc<SelectorRow>(num_selectors_);
552 for (intptr_t i = 0; i < num_selectors_; i++) {
553 TableSelector* selector = &selector_map_.selectors_[i];
554 new (&selector_rows[i]) SelectorRow(Z, selector);
555 if (selector->called_on_null && !selector->on_null_interface) {
556 selector_rows[i].DefineSelectorImplementationForInterval(
557 kNullCid, 0, Interval(kNullCid, kNullCid + 1), nullptr);
558 }
559 }
560
561 // Add implementation intervals to the selector rows for all classes that
562 // have concrete implementations of the selector.
563 for (classid_t cid = kIllegalCid + 1; cid < num_classes_; cid++) {
564 obj = classes_->At(cid);
565 if (obj.IsClass()) {
566 klass = Class::RawCast(obj.ptr());
567 GrowableArray<Interval>& subclass_cid_ranges = cid_subclass_ranges[cid];
568
569 functions = klass.current_functions();
570 if (!functions.IsNull()) {
571 const int16_t depth = cid_depth[cid];
572 for (intptr_t j = 0; j < functions.Length(); j++) {
573 function ^= functions.At(j);
574 if (function.IsDynamicFunction(/*allow_abstract=*/false)) {
575 const int32_t sid = selector_map_.SelectorId(function);
576
577 if (sid != SelectorMap::kInvalidSelectorId) {
578 auto MakeIntervals = [&](const Function& function, int32_t sid) {
579 // A function handle that survives until the table is built.
580 auto& function_handle = Function::ZoneHandle(Z, function.ptr());
581
582 for (intptr_t i = 0; i < subclass_cid_ranges.length(); i++) {
583 Interval& subclass_cid_range = subclass_cid_ranges[i];
584 selector_rows[sid].DefineSelectorImplementationForInterval(
585 cid, depth, subclass_cid_range, &function_handle);
586 }
587 };
588 MakeIntervals(function, sid);
589
590 if (selector_map_.selectors_[sid].torn_off) {
591 const String& method_name = String::Handle(Z, function.name());
592 const String& getter_name =
593 String::Handle(Z, Field::GetterName(method_name));
594 const Function& tearoff = Function::Handle(
595 Z, function.GetMethodExtractor(getter_name));
596 const int32_t tearoff_sid = selector_map_.SelectorId(tearoff);
597
598 if (tearoff_sid != SelectorMap::kInvalidSelectorId) {
599 MakeIntervals(tearoff, tearoff_sid);
600 }
601 }
602 }
603 }
604 }
605 }
606 }
607 }
608
609 // Retain all selectors that contain implementation intervals.
610 for (intptr_t i = 0; i < num_selectors_; i++) {
611 const TableSelector& selector = selector_map_.selectors_[i];
612 if (selector.IsUsed() && selector_rows[i].Finalize()) {
613 table_rows_.Add(&selector_rows[i]);
614 }
615 }
616}
617
618void DispatchTableGenerator::ComputeSelectorOffsets() {
619 ASSERT(table_rows_.length() > 0);
620
621 RowFitter fitter;
622
623 // Sort the table rows according to popularity, descending.
624 struct PopularitySorter {
625 static int Compare(SelectorRow* const* a, SelectorRow* const* b) {
626 return (*b)->CallCount() - (*a)->CallCount();
627 }
628 };
629 table_rows_.Sort(PopularitySorter::Compare);
630
631 // Try to allocate at optimal offset.
632 const int32_t optimal_offset = DispatchTable::kOriginElement;
633 for (intptr_t i = 0; i < table_rows_.length(); i++) {
634 fitter.FitAndAllocate(table_rows_[i], optimal_offset, optimal_offset);
635 }
636
637 // Sort the table rows according to popularity / size, descending.
638 struct PopularitySizeRatioSorter {
639 static int Compare(SelectorRow* const* a, SelectorRow* const* b) {
640 return (*b)->CallCount() * (*a)->total_size() -
641 (*a)->CallCount() * (*b)->total_size();
642 }
643 };
644 table_rows_.Sort(PopularitySizeRatioSorter::Compare);
645
646 // Try to allocate at small offsets.
647 const int32_t max_offset = DispatchTable::kLargestSmallOffset;
648 for (intptr_t i = 0; i < table_rows_.length(); i++) {
649 fitter.FitAndAllocate(table_rows_[i], 0, max_offset);
650 }
651
652 // Sort the table rows according to size, descending.
653 struct SizeSorter {
654 static int Compare(SelectorRow* const* a, SelectorRow* const* b) {
655 return (*b)->total_size() - (*a)->total_size();
656 }
657 };
658 table_rows_.Sort(SizeSorter::Compare);
659
660 // Allocate remaining rows at large offsets.
661 const int32_t min_large_offset = DispatchTable::kLargestSmallOffset + 1;
662 for (intptr_t i = 0; i < table_rows_.length(); i++) {
663 fitter.FitAndAllocate(table_rows_[i], min_large_offset);
664 }
665
666 table_size_ = fitter.TableSize();
667}
668
669ArrayPtr DispatchTableGenerator::BuildCodeArray() {
670 auto& entries = Array::Handle(zone_, Array::New(table_size_, Heap::kOld));
671 for (intptr_t i = 0; i < table_rows_.length(); i++) {
672 table_rows_[i]->FillTable(classes_, entries);
673 }
674 entries.MakeImmutable();
675 return entries.ptr();
676}
677
678} // namespace compiler
679} // namespace dart
680
681#endif // defined(DART_PRECOMPILER)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
static size_t total_size(SkSBlockAllocator< N > &pool)
SI F table(const skcms_Curve *curve, F v)
#define Z
const TableSelector * GetSelector(const Function &interface_target) const
static const char * begin(const StringSlice &s)
Definition editor.cpp:252
#define ASSERT(E)
static bool b
struct MyStruct a[10]
#define FATAL(error)
glong glong end
uint8_t value
Dart_NativeFunction function
Definition fuchsia.cc:51
#define HANDLESCOPE(thread)
Definition handles.h:321
size_t length
bool Contains(const Container &container, const Value &value)
int32_t classid_t
Definition globals.h:524
@ kIllegalCid
Definition class_id.h:214
const intptr_t cid
Point offset