feat(tvix/vm): implement construction of optimised KV attrsets
For name/value pairs (which occur extremely often in Nix and make up a significant chunk of the runtime cost of evaluating nixpkgs) we substitute an optimised representation. For now this will only be used if the name/value pair keys were specified as literal identifiers or strings (i.e. if chunks are encountered as keys they are not forced and a normal attribute set backed by a map will be constructed). Change-Id: Ic79746c323e627528bd58b1a6024ee8d0aff7858 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6102 Reviewed-by: grfn <grfn@gws.fyi> Tested-by: BuildkiteCI
This commit is contained in:
parent
e876c3a41c
commit
ec1770f95a
1 changed files with 77 additions and 0 deletions
|
@ -25,6 +25,10 @@ impl VM {
|
||||||
self.stack.pop().expect("TODO")
|
self.stack.pop().expect("TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn peek(&self, at: usize) -> &Value {
|
||||||
|
&self.stack[self.stack.len() - 1 - at]
|
||||||
|
}
|
||||||
|
|
||||||
fn pop_number_pair(&mut self) -> EvalResult<NumberPair> {
|
fn pop_number_pair(&mut self) -> EvalResult<NumberPair> {
|
||||||
let v2 = self.pop();
|
let v2 = self.pop();
|
||||||
let v1 = self.pop();
|
let v1 = self.pop();
|
||||||
|
@ -128,6 +132,79 @@ impl VM {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_attrset(&mut self, count: usize) -> EvalResult<()> {
|
fn run_attrset(&mut self, count: usize) -> EvalResult<()> {
|
||||||
|
// If the attribute count happens to be 2, we might be able to
|
||||||
|
// create the optimised name/value struct instead.
|
||||||
|
if count == 2 {
|
||||||
|
// When determining whether we are dealing with a
|
||||||
|
// name/value pair, we return the stack locations of name
|
||||||
|
// and value, using `0` as a sentinel value (i.e. if
|
||||||
|
// either is 0, we are dealing with some other attrset).
|
||||||
|
let is_pair = {
|
||||||
|
// The keys are located 1 & 3 values back in the
|
||||||
|
// stack.
|
||||||
|
let k1 = self.peek(1);
|
||||||
|
let k2 = self.peek(3);
|
||||||
|
|
||||||
|
match (k1, k2) {
|
||||||
|
(Value::String(NixString(s1)), Value::String(NixString(s2)))
|
||||||
|
if (s1 == "name" && s2 == "value") =>
|
||||||
|
{
|
||||||
|
(1, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
(Value::String(NixString(s1)), Value::String(NixString(s2)))
|
||||||
|
if (s1 == "value" && s2 == "name") =>
|
||||||
|
{
|
||||||
|
(2, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Technically this branch lets type errors pass,
|
||||||
|
// but they will be caught during normal attribute
|
||||||
|
// set construction instead.
|
||||||
|
_ => (0, 0),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match is_pair {
|
||||||
|
(1, 2) => {
|
||||||
|
// The value of 'name' is at stack slot 0, the
|
||||||
|
// value of 'value' is at stack slot 2.
|
||||||
|
let pair = Value::Attrs(Rc::new(NixAttrs::KV {
|
||||||
|
name: self.pop(),
|
||||||
|
value: {
|
||||||
|
self.pop(); // ignore the key
|
||||||
|
self.pop()
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Clean up the last key fragment.
|
||||||
|
self.pop();
|
||||||
|
|
||||||
|
self.push(pair);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
(2, 1) => {
|
||||||
|
// The value of 'name' is at stack slot 2, the
|
||||||
|
// value of 'value' is at stack slot 0.
|
||||||
|
let pair = Value::Attrs(Rc::new(NixAttrs::KV {
|
||||||
|
value: self.pop(),
|
||||||
|
name: {
|
||||||
|
self.pop(); // ignore the key
|
||||||
|
self.pop()
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Clean up the last key fragment.
|
||||||
|
self.pop();
|
||||||
|
|
||||||
|
self.push(pair);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut attrs: BTreeMap<NixString, Value> = BTreeMap::new();
|
let mut attrs: BTreeMap<NixString, Value> = BTreeMap::new();
|
||||||
|
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
|
|
Loading…
Reference in a new issue