Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc optimizations that address some of the XXX comments #202

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions src/engine/CodeValidator.v3
Original file line number Diff line number Diff line change
Expand Up @@ -1318,10 +1318,18 @@ class CodeValidator(extensions: Extension.set, limits: Limits, module: Module, e
var p = labelArgs(target);
if (p == null) return;
var ct = if(c.tag == null, SigCache.arr_v, c.tag.fields);
if (c.exnref) ct = Arrays.append(ValueTypes.EXNREF, ct); // XXX: avoid array copy
if (ct.length != p.length) return err_atpc().ArityMismatchInHandler("catch", i, p.length, ct.length);
for (j < p.length) {
if (!ValueTypes.isAssignable(ct[j], p[j])) err_atpc().TypeMismatchInHandler("catch", i, p[i], ct[i]);

// If `c.exnref` is true, then `ValueTypes.EXNREF` is appended. This is
// simulated without an extra array copy of the signatures.
var actual_ct_length = if(c.exnref, ct.length + 1, ct.length);
if (actual_ct_length != p.length) {
return err_atpc().ArityMismatchInHandler("catch", i, p.length, actual_ct_length);
}
for (j < ct.length) {
if (!ValueTypes.isAssignable(ct[j], p[j])) err_atpc().TypeMismatchInHandler("catch", i, p[j], ct[j]);
}
if (c.exnref && !ValueTypes.isAssignable(ValueTypes.EXNREF, p[p.length - 1])) {
err_atpc().TypeMismatchInHandler("catch", i, p[p.length - 1], ValueTypes.EXNREF);
}
}
def readAndCheckContHandlerTable(cont: ContDecl) {
Expand Down
26 changes: 11 additions & 15 deletions src/engine/Sidetable.v3
Original file line number Diff line number Diff line change
Expand Up @@ -58,39 +58,35 @@ component Sidetables {
// Implements an efficient mapping from program counter to sidetable position.
// The sidetable itself is stored in a {FuncDecl} without this mapping (to save space).
class SidetableMap(func: FuncDecl) {
private def pairs = Vector<PcPair>.new();
private var pairs: Array<PcPair>;

new() {
var bi = BytecodeIterator.new().reset(func);
var visitor = SidetableMapperVisitor.new(this, bi);
var builder = Vector<PcPair>.new();
var visitor = SidetableMapperVisitor.new(builder, bi);
while (bi.more()) {
bi.dispatch(visitor);
bi.next();
}
pairs = builder.extract();
}

// Get the sidetable entry position
def [pc: int] -> int {
// XXX: use a binary search
var i = 0;
var stp = 0;
while (i < pairs.length) {
var e = pairs[i];
if (e.pc > pc) break;
stp = e.stp;
i++;
}
return stp;
var upperbound = RangeUtil.upperbound(pairs, PcPair(pc, -1), PcPair.cmp_pc);
return if (upperbound == 0, 0, pairs[upperbound - 1].stp);
}
}

private type PcPair(pc: int, stp: int) #unboxed { }
private class SidetableMapperVisitor(map: SidetableMap, bi: BytecodeIterator) extends BytecodeVisitor {
private type PcPair(pc: int, stp: int) #unboxed {
def cmp_pc(other: PcPair) -> bool { return pc < other.pc; }
}
private class SidetableMapperVisitor(pairs: Vector<PcPair>, bi: BytecodeIterator) extends BytecodeVisitor {
var cur: int;

def entry(size: int) {
cur += size / 4;
map.pairs.put(PcPair(bi.pc + 1, cur));
pairs.put(PcPair(bi.pc + 1, cur));
}

def visit_IF(btc: BlockTypeCode) { entry(Sidetable_GotoEntry.size); }
Expand Down
11 changes: 3 additions & 8 deletions src/engine/v3/V3Interpreter.v3
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class V3Interpreter extends WasmStack {
if (args.length == 0 && state_ == StackState.RESUMABLE) return;
checkState("bind()", StackState.SUSPENDED);
if (params_arity < args.length) fatal(Strings.format2("bind() expected %d arguments, got %d", params_arity, args.length));
for (v in args) values.push(v); // XXX: ArrayStack.pushr
values.pushr(args);
params_arity -= args.length;
if (params_arity == 0) state_ = StackState.RESUMABLE;
}
Expand Down Expand Up @@ -657,13 +657,8 @@ class V3Interpreter extends WasmStack {
}
BR_ON_NULL => {
var v = pop();
if (Values.isNull(v)) { // XXX: use doBranch
codeptr.at(doGoto(pc));
} else {
codeptr.skip_label();
push(v);
doFallthru();
}
if (!Values.isNull(v)) push(v);
doBranch(pc, Values.isNull(v));
}
REF_EQ => {
var a = pop();
Expand Down
18 changes: 11 additions & 7 deletions src/monitors/ControlInstrumenter.v3
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,19 @@ class ControlInstrumentation(func: FuncDecl, entries: Array<(int, Probe)>) {
return if (e.0 == 0, CountProbe.!(e.1));
}
def getCount(pc: int) -> CountProbe {
// TODO: binary search
for (i < entries.length) {
var lower = RangeUtil.lowerbound(entries, (pc, null), cmp_lt_pc);
var upper = RangeUtil.upperbound(entries, (pc, null), cmp_lt_pc);
for (i = lower; i < upper; i++) {
var e = entries[i];
if (e.0 == pc && CountProbe.?(e.1)) return CountProbe.!(e.1);
if (CountProbe.?(e.1)) return CountProbe.!(e.1);
}
return null;
}
def getBrCounts(pc: int) -> Array<u64> {
// TODO: binary search
for (i < entries.length) {
var e = entries[i];
if (e.0 == pc) match (e.1) {
var lower = RangeUtil.lowerbound(entries, (pc, null), cmp_lt_pc);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're looking for an exact match, I think it makes sense to have a utility method that specifically finds an exact match.

var upper = RangeUtil.upperbound(entries, (pc, null), cmp_lt_pc);
for (i = lower; i < upper; i++) {
match (entries[i].1) {
x: CiBrProbe => return x.taken;
x: CiBrOnNullProbe => return x.taken;
x: CiBrOnCastProbe => return x.taken;
Expand All @@ -104,6 +105,9 @@ class ControlInstrumentation(func: FuncDecl, entries: Array<(int, Probe)>) {
}
return false;
}
private def cmp_lt_pc(a: (int, Probe), b: (int, Probe)) -> bool {
return a.0 < b.0;
}
}
def nonzero(x: Array<u64>) -> bool {
for (v in x) if (v > 0) return true;
Expand Down
46 changes: 46 additions & 0 deletions src/util/RangeUtil.v3
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2020 Ben L. Titzer. All rights reserved.
// See LICENSE for details of Apache 2.0 license.

// Utility methods for vector.
//
// TODO: all these searches can be done on Range<T>. Maybe add a `toRange`
// method to `Vector` in virgil and change this to `RangeSearchUtil`? Vector<T>
// to Range<T> should be pretty much free.
component RangeUtil {
// Assumes that the vector is sorted.
def binarySearch<T>(range: Range<T>, val: T, lt: (T, T) -> bool) -> int {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this method. I think it should live in Virgil's lib/util and have some unittests.

var l = 0, r = range.length;
while (l < r) {
var mid = (l + r) >> 1;
var mid_val = range[mid];
if (lt(val, mid_val)) r = mid - 1;
else if (lt(mid_val, val)) l = mid + 1;
else return mid;
}
return -1;
}
// Returns the *first* element that is *not less* than `val`.
def lowerbound<T>(range: Range<T>, val: T, lt: (T, T) -> bool) -> int {
var start = 0;
var count = range.length;
while (count > 0) {
var step = count >> 1;
var mid = start + step;
if (lt(range[mid], val)) { start = mid + 1; count -= step + 1; }
else count = step;
}
return start;
}
// Returns the *first* element that is *greater* than `val`.
def upperbound<T>(range: Range<T>, val: T, lt: (T, T) -> bool) -> int {
var start = 0;
var count = range.length;
while (count > 0) {
var step = count >> 1;
var mid = start + step;
if (!lt(val, range[mid])) { start = mid + 1; count -= step + 1; }
else count = step;
}
return start;
}
}
Loading