refactor(tvix/eval): improve representation of chunk/span mapping
This switches out the previous compressed representation (count of instructions per span) with a representation where the chunk's span list stores the index of the first operation that belongs to a span, and finds the right span by using a binary search when looking them up. This improves the lookup complexity from O(n) to O(log n). This improvement was suggested and (mostly) implemented by GPT-4. I only fixed up some names and updated the logic for deleting spans (which it only did not do because I didn't tell it about that). The code was verified by producing a complex error before/after the change and ensuring that all spans in the error match exactly. Co-Authored-By: GPT-4 Change-Id: Ibfa12cc6973af1c9b0ae55bb464d1975209771f5 Reviewed-on: https://cl.tvl.fyi/c/depot/+/8385 Reviewed-by: ezemtsov <eugene.zemtsov@gmail.com> Tested-by: BuildkiteCI Autosubmit: tazjin <tazjin@tvl.su>
This commit is contained in:
parent
5bf9134324
commit
ce502bdc89
1 changed files with 22 additions and 24 deletions
|
@ -21,8 +21,8 @@ struct SourceSpan {
|
||||||
/// Span into the [codemap::Codemap].
|
/// Span into the [codemap::Codemap].
|
||||||
span: codemap::Span,
|
span: codemap::Span,
|
||||||
|
|
||||||
/// Number of instructions derived from this span.
|
/// Index of the first operation covered by this span.
|
||||||
count: usize,
|
start: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A chunk is a representation of a sequence of bytecode
|
/// A chunk is a representation of a sequence of bytecode
|
||||||
|
@ -61,7 +61,7 @@ impl Chunk {
|
||||||
pub fn push_op(&mut self, data: OpCode, span: codemap::Span) -> CodeIdx {
|
pub fn push_op(&mut self, data: OpCode, span: codemap::Span) -> CodeIdx {
|
||||||
let idx = self.code.len();
|
let idx = self.code.len();
|
||||||
self.code.push(data);
|
self.code.push(data);
|
||||||
self.push_span(span);
|
self.push_span(span, idx);
|
||||||
CodeIdx(idx)
|
CodeIdx(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,19 +76,11 @@ impl Chunk {
|
||||||
// Simply drop the last op.
|
// Simply drop the last op.
|
||||||
self.code.pop();
|
self.code.pop();
|
||||||
|
|
||||||
// If the last span only had this op, drop it, otherwise
|
if let Some(span) = self.spans.last() {
|
||||||
// decrease its operation counter.
|
// If the last span started at this op, drop it.
|
||||||
match self.spans.last_mut() {
|
if span.start == self.code.len() {
|
||||||
// If the last span had more than one op, decrease the
|
|
||||||
// counter.
|
|
||||||
Some(span) if span.count > 1 => span.count -= 1,
|
|
||||||
|
|
||||||
// Otherwise, drop it.
|
|
||||||
Some(_) => {
|
|
||||||
self.spans.pop();
|
self.spans.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
None => unreachable!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,31 +92,37 @@ impl Chunk {
|
||||||
|
|
||||||
// Span tracking implementation
|
// Span tracking implementation
|
||||||
|
|
||||||
fn push_span(&mut self, span: codemap::Span) {
|
fn push_span(&mut self, span: codemap::Span, start: usize) {
|
||||||
match self.spans.last_mut() {
|
match self.spans.last_mut() {
|
||||||
// We do not need to insert the same span again, as this
|
// We do not need to insert the same span again, as this
|
||||||
// instruction was compiled from the same span as the last
|
// instruction was compiled from the same span as the last
|
||||||
// one.
|
// one.
|
||||||
Some(last) if last.span == span => last.count += 1,
|
Some(last) if last.span == span => {}
|
||||||
|
|
||||||
// In all other cases, this is a new source span.
|
// In all other cases, this is a new source span.
|
||||||
_ => self.spans.push(SourceSpan { span, count: 1 }),
|
_ => self.spans.push(SourceSpan { span, start }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the [codemap::Span] from which the instruction at
|
/// Retrieve the [codemap::Span] from which the instruction at
|
||||||
/// `offset` was compiled.
|
/// `offset` was compiled.
|
||||||
pub fn get_span(&self, offset: CodeIdx) -> codemap::Span {
|
pub fn get_span(&self, offset: CodeIdx) -> codemap::Span {
|
||||||
let mut pos = 0;
|
let position = self
|
||||||
|
.spans
|
||||||
|
.binary_search_by(|span| span.start.cmp(&offset.0));
|
||||||
|
|
||||||
for span in &self.spans {
|
let span = match position {
|
||||||
pos += span.count;
|
Ok(index) => &self.spans[index],
|
||||||
if pos > offset.0 {
|
Err(index) => {
|
||||||
return span.span;
|
if index == 0 {
|
||||||
|
&self.spans[0]
|
||||||
|
} else {
|
||||||
|
&self.spans[index - 1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
panic!("compiler error: chunk missing span for offset {}", offset.0);
|
span.span
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the disassembler representation of the operation at
|
/// Write the disassembler representation of the operation at
|
||||||
|
|
Loading…
Reference in a new issue