Skip to content

Commit

Permalink
[dart2wasm] Make generic class-id range checking functionality and su…
Browse files Browse the repository at this point in the history
…pport binary search

This will be taken advantage of more in future CLs.

Change-Id: I1f113c17af590144d9e4ca74b289f9d030b45066
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/374944
Reviewed-by: Srujan Gaddam <[email protected]>
Commit-Queue: Martin Kustermann <[email protected]>
  • Loading branch information
mkustermann authored and Commit Queue committed Jul 13, 2024
1 parent b7cc6de commit 88b6ee2
Showing 1 changed file with 139 additions and 25 deletions.
164 changes: 139 additions & 25 deletions pkg/dart2wasm/lib/code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3958,45 +3958,159 @@ enum _VirtualCallKind {
extension MacroAssembler on w.InstructionsBuilder {
/// `[i32] -> [i32]`
///
/// Consumes an `i32` for a class ID, leaves an `i32` as `bool` for whether
/// Consumes a `i32` class ID, leaves an `i32` as `bool` for whether
/// the class ID is in the given list of ranges.
void emitClassIdRangeCheck(List<Range> ranges) {
if (ranges.isEmpty) {
drop();
final rangeValues = ranges.map((r) => (range: r, value: null)).toList();
classIdSearch<Null>(rangeValues, [w.NumType.i32], (_) {
i32_const(1);
}, () {
i32_const(0);
} else if (ranges.length == 1) {
final range = ranges[0];
});
}

i32_const(range.start);
if (range.length == 1) {
i32_eq();
} else {
i32_sub();
i32_const(range.length);
i32_lt_u();
/// `[i32] -> [outputs]`
///
/// Consumes a `i32` class ID and checks whether it lies within one of the
/// given [ranges] using a linear or binary search.
///
/// The [ranges] have to be non-empty, non-overlapping and sorted.
///
/// Calls [match] on a matching value and [miss] if provided and no match was
/// found.
///
/// Assumes [match] and [miss] leave [outputs] on the stack.
void classIdSearch<T>(
List<({Range range, T value})> ranges,
List<w.ValueType> outputs,
void Function(T) match,
void Function()? miss) {
final bool linearSearch = ranges.length <= 3;
if (traceEnabled) {
comment('Class id ${linearSearch ? 'linear' : 'binary'} search:');
for (final (:range, :value) in ranges) {
comment(' - $range -> $value');
}
}
if (linearSearch) {
_linearClassIdSearch<T>(ranges, outputs, match, miss);
} else {
w.Local idLocal = addLocal(w.NumType.i32, isParameter: false);
local_set(idLocal);
w.Label done = block(const [], const [w.NumType.i32]);
i32_const(1);
_binaryClassIdSearch<T>(ranges, outputs, match, miss);
}
}

void _binaryClassIdSearch<T>(
List<({Range range, T value})> ranges,
List<w.ValueType> outputs,
void Function(T) match,
void Function()? miss) {
assert(ranges.isNotEmpty || miss != null);
if (miss != null && ranges.isEmpty) {
drop();
miss();
return;
}

for (Range range in ranges) {
local_get(idLocal);
i32_const(range.start);
w.Local classId = addLocal(w.NumType.i32, isParameter: false);
local_set(classId);

final done = block([], outputs);
final fail = block();
void search(int left, int right, Range searchArea) {
if (left == right) {
final entry = ranges[left];
final range = entry.range;
assert(searchArea.containsRange(range));
if (miss == null || range.containsRange(searchArea)) {
match(entry.value);
br(done);
return;
}
local_get(classId);
if (range.length == 1) {
i32_const(range.start);
i32_eq();
} else {
i32_sub();
i32_const(range.length);
i32_lt_u();
if (searchArea.end <= range.end) {
i32_const(range.start);
i32_ge_u();
} else if (range.start <= searchArea.start) {
i32_const(range.end);
i32_le_u();
} else {
i32_const(range.start);
i32_sub();
i32_const(range.length);
i32_lt_u();
}
}
br_if(done);
if_();
match(entry.value);
br(done);
end();
br(fail);
return;
}
final mid = (left + right) ~/ 2;
final midRange = ranges[mid].range;

local_get(classId);
i32_const(midRange.end);
i32_le_u();
if_();
search(left, mid, Range(searchArea.start, midRange.end));
end();
search(mid + 1, right, Range(midRange.end + 1, searchArea.end));
}

search(0, ranges.length - 1, Range(0, 0xffffffff));
end(); // fail
if (miss != null) {
miss();
br(done);
} else {
unreachable();
}
end(); // done
}

void _linearClassIdSearch<T>(
List<({Range range, T value})> ranges,
List<w.ValueType> outputs,
void Function(T) match,
void Function()? miss) {
assert(ranges.isNotEmpty || miss != null);
if (miss != null && ranges.isEmpty) {
drop();
i32_const(0);
end(); // done
miss();
return;
}

w.Local classId = addLocal(w.NumType.i32, isParameter: false);
local_set(classId);
final done = block([], outputs);
for (final (:range, :value) in ranges) {
local_get(classId);
i32_const(range.start);
if (range.length == 1) {
i32_eq();
} else {
i32_sub();
i32_const(range.length);
i32_lt_u();
}
if_();
match(value);
br(done);
end();
}
if (miss != null) {
miss();
br(done);
} else {
unreachable();
}
end(); // done
}

/// `[ref _Closure] -> [i32]`
Expand Down

0 comments on commit 88b6ee2

Please sign in to comment.