Skip to content

Commit de76c39

Browse files
committed
aml: improve interpreter docs + few bits and bobs
1 parent 2561e5a commit de76c39

File tree

1 file changed

+39
-37
lines changed

1 file changed

+39
-37
lines changed

aml/src/lib.rs

+39-37
Original file line numberDiff line numberDiff line change
@@ -111,32 +111,27 @@ where
111111

112112
fn do_execute_method(&self, mut context: MethodContext) -> Result<Arc<Object>, AmlError> {
113113
/*
114-
* TODO
114+
* This is the main loop that executes operations. Every op is handled at the top-level of
115+
* the loop to prevent pathological stack growth from nested operations.
115116
*
116-
* This is the main loop that executes ops. Every op is handled at the top-level loop to
117-
* prevent pathological stack depths.
117+
* The loop has three main stages:
118+
* 1) Check if any in-flight operations are ready to be executed (i.e. have collected all
119+
* their arguments). An operation completing may contribute the last required argument
120+
* of the one above, so this is repeated for as many operations as are ready to be
121+
* retired.
122+
* 2) Look at the next opcode in the stream. If we've run out of opcodes in the current
123+
* block, run logic to determine where in the stream we should move to next. Special
124+
* logic at this level handles things like moving in/out of package definitions, and
125+
* performing control flow.
126+
* 3) When the next opcode is determined, use it to interpret the next portion of the
127+
* stream. If that is data, the correct number of bytes can be consumed and
128+
* contributed to the current in-flight operation. If it's an opcode, a new in-flight
129+
* operation is started, and we go round the loop again.
118130
*
119-
* Worked example: AddOp TermArg TermArg Target
120-
* - We go round the loop 4 times to interpret this.
121-
* - We encounter the AddOp. We add a new in-flight operation with 3 expected arguments.
122-
* - We go round again and find some sort of TermArg. This may create new in-flight ops
123-
* which we then go round again to complete. Once we're finished, we add this to the
124-
* AddOp's record. We then need to detect that all 3 arguments are ready and retire the
125-
* AddOp *before* moving on.
126-
*
127-
* My thoughts are that we can go round and round a loop with two big stages. First, we
128-
* check if in-flight ops are ready to be executed (e.g. have collected all their
129-
* arguments) and execute them. This can in turn complete the next op up, so this should
130-
* complete as many in-flight ops as possible at a time.
131-
*
132-
* Once all possible in-flight ops have been executed, we then need to move forward in the
133-
* stream. Depending on the next op, this could create a new in-flight op that is then
134-
* pre-empted, or could parse an argument to an existing in-flight op, which may then be
135-
* able to be completed. This stage should not do any executing in its own right, really.
136-
* It's just about consuming the next however-many bytes of the stream and setting things
137-
* up.
131+
* This scheme is what allows the interpreter to use a loop that somewhat resembles a
132+
* traditional fast bytecode VM, but also provides enough flexibility to handle the
133+
* quirkier parts of the AML grammar, particularly the left-to-right encoding of operands.
138134
*/
139-
140135
loop {
141136
/*
142137
* First, see if we've gathered enough arguments to complete some in-flight operations.
@@ -207,7 +202,7 @@ where
207202
Arc::new(Object::Buffer(buffer))
208203
};
209204
// TODO: use potentially-updated result for return value here
210-
self.do_store(&mut context, target, result.clone())?;
205+
self.do_store(target, result.clone())?;
211206
context.contribute_arg(Argument::Object(result));
212207
}
213208
Opcode::FromBCD => self.do_from_bcd(&mut context, op)?,
@@ -393,7 +388,7 @@ where
393388
}
394389
Opcode::Store => {
395390
let [Argument::Object(object), target] = &op.arguments[..] else { panic!() };
396-
self.do_store(&mut context, &target, object.clone())?;
391+
self.do_store(&target, object.clone())?;
397392
}
398393
Opcode::RefOf => {
399394
let [Argument::Object(object)] = &op.arguments[..] else { panic!() };
@@ -408,7 +403,7 @@ where
408403
} else {
409404
let reference =
410405
Arc::new(Object::Reference { kind: ReferenceKind::RefOf, inner: object.clone() });
411-
self.do_store(&mut context, target, reference)?;
406+
self.do_store(target, reference)?;
412407
Object::Integer(u64::MAX)
413408
};
414409
context.contribute_arg(Argument::Object(Arc::new(result)));
@@ -1193,7 +1188,7 @@ where
11931188
Opcode::Multiply => left.wrapping_mul(right),
11941189
Opcode::Divide => {
11951190
if let Some(remainder) = target2 {
1196-
self.do_store(context, remainder, Arc::new(Object::Integer(left.wrapping_rem(right))))?;
1191+
self.do_store(remainder, Arc::new(Object::Integer(left.wrapping_rem(right))))?;
11971192
}
11981193
left.wrapping_div_euclid(right)
11991194
}
@@ -1210,7 +1205,7 @@ where
12101205

12111206
let result = Arc::new(Object::Integer(result));
12121207
// TODO: use result for arg
1213-
self.do_store(context, target, result.clone())?;
1208+
self.do_store(target, result.clone())?;
12141209
context.contribute_arg(Argument::Object(result));
12151210
Ok(())
12161211
}
@@ -1360,7 +1355,7 @@ where
13601355
_ => Err(AmlError::InvalidOperationOnObject { op: Operation::Mid, typ: source.typ() })?,
13611356
});
13621357

1363-
self.do_store(context, target, result.clone())?;
1358+
self.do_store(target, result.clone())?;
13641359
context.contribute_arg(Argument::Object(result));
13651360

13661361
Ok(())
@@ -1417,7 +1412,7 @@ where
14171412
}
14181413
};
14191414
// TODO: use result of store
1420-
self.do_store(context, target, result.clone())?;
1415+
self.do_store(target, result.clone())?;
14211416
context.contribute_arg(Argument::Object(result));
14221417
Ok(())
14231418
}
@@ -1511,21 +1506,22 @@ where
15111506
_ => Err(AmlError::IndexOutOfBounds)?,
15121507
});
15131508

1514-
self.do_store(context, target, result.clone())?;
1509+
self.do_store(target, result.clone())?;
15151510
context.contribute_arg(Argument::Object(result));
15161511
Ok(())
15171512
}
1518-
fn do_store(
1519-
&self,
1520-
context: &mut MethodContext,
1521-
target: &Argument,
1522-
object: Arc<Object>,
1523-
) -> Result<(), AmlError> {
1513+
1514+
// TODO: this might actually do weird stuff to your data if written to a field with BufferAcc
1515+
// access. I guess we need to return something here really and use it instead of the result
1516+
// when returning?? We need to look carefully at all use-sites to make sure it actually returns
1517+
// the result of the store, not the object it passed to us.
1518+
fn do_store(&self, target: &Argument, object: Arc<Object>) -> Result<(), AmlError> {
15241519
// TODO: find the destination (need to handle references, debug objects, etc.)
15251520
// TODO: convert object to be of the type of destination, in line with 19.3.5 of the spec
15261521
// TODO: write the object to the destination, including e.g. field writes that then lead to
15271522
// literally god knows what.
15281523
let object = object.unwrap_transparent_reference();
1524+
15291525
match target {
15301526
Argument::Object(target) => match target.gain_mut() {
15311527
Object::Integer(target) => match object.gain_mut() {
@@ -2581,6 +2577,12 @@ mod tests {
25812577
fn release(&self, _mutex: Handle) {}
25822578
}
25832579

2580+
#[test]
2581+
fn verify_interpreter_send_sync() {
2582+
fn test_send_sync<T: Send + Sync>() {}
2583+
test_send_sync::<Interpreter<TestHandler>>();
2584+
}
2585+
25842586
#[test]
25852587
fn add_op() {
25862588
let interpreter = Interpreter::new(TestHandler, 2);

0 commit comments

Comments
 (0)