@@ -111,32 +111,27 @@ where
111
111
112
112
fn do_execute_method ( & self , mut context : MethodContext ) -> Result < Arc < Object > , AmlError > {
113
113
/*
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.
115
116
*
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.
118
130
*
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.
138
134
*/
139
-
140
135
loop {
141
136
/*
142
137
* First, see if we've gathered enough arguments to complete some in-flight operations.
@@ -207,7 +202,7 @@ where
207
202
Arc :: new ( Object :: Buffer ( buffer) )
208
203
} ;
209
204
// 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 ( ) ) ?;
211
206
context. contribute_arg ( Argument :: Object ( result) ) ;
212
207
}
213
208
Opcode :: FromBCD => self . do_from_bcd ( & mut context, op) ?,
@@ -393,7 +388,7 @@ where
393
388
}
394
389
Opcode :: Store => {
395
390
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 ( ) ) ?;
397
392
}
398
393
Opcode :: RefOf => {
399
394
let [ Argument :: Object ( object) ] = & op. arguments [ ..] else { panic ! ( ) } ;
@@ -408,7 +403,7 @@ where
408
403
} else {
409
404
let reference =
410
405
Arc :: new ( Object :: Reference { kind : ReferenceKind :: RefOf , inner : object. clone ( ) } ) ;
411
- self . do_store ( & mut context , target, reference) ?;
406
+ self . do_store ( target, reference) ?;
412
407
Object :: Integer ( u64:: MAX )
413
408
} ;
414
409
context. contribute_arg ( Argument :: Object ( Arc :: new ( result) ) ) ;
@@ -1193,7 +1188,7 @@ where
1193
1188
Opcode :: Multiply => left. wrapping_mul ( right) ,
1194
1189
Opcode :: Divide => {
1195
1190
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) ) ) ) ?;
1197
1192
}
1198
1193
left. wrapping_div_euclid ( right)
1199
1194
}
@@ -1210,7 +1205,7 @@ where
1210
1205
1211
1206
let result = Arc :: new ( Object :: Integer ( result) ) ;
1212
1207
// TODO: use result for arg
1213
- self . do_store ( context , target, result. clone ( ) ) ?;
1208
+ self . do_store ( target, result. clone ( ) ) ?;
1214
1209
context. contribute_arg ( Argument :: Object ( result) ) ;
1215
1210
Ok ( ( ) )
1216
1211
}
@@ -1360,7 +1355,7 @@ where
1360
1355
_ => Err ( AmlError :: InvalidOperationOnObject { op : Operation :: Mid , typ : source. typ ( ) } ) ?,
1361
1356
} ) ;
1362
1357
1363
- self . do_store ( context , target, result. clone ( ) ) ?;
1358
+ self . do_store ( target, result. clone ( ) ) ?;
1364
1359
context. contribute_arg ( Argument :: Object ( result) ) ;
1365
1360
1366
1361
Ok ( ( ) )
@@ -1417,7 +1412,7 @@ where
1417
1412
}
1418
1413
} ;
1419
1414
// TODO: use result of store
1420
- self . do_store ( context , target, result. clone ( ) ) ?;
1415
+ self . do_store ( target, result. clone ( ) ) ?;
1421
1416
context. contribute_arg ( Argument :: Object ( result) ) ;
1422
1417
Ok ( ( ) )
1423
1418
}
@@ -1511,21 +1506,22 @@ where
1511
1506
_ => Err ( AmlError :: IndexOutOfBounds ) ?,
1512
1507
} ) ;
1513
1508
1514
- self . do_store ( context , target, result. clone ( ) ) ?;
1509
+ self . do_store ( target, result. clone ( ) ) ?;
1515
1510
context. contribute_arg ( Argument :: Object ( result) ) ;
1516
1511
Ok ( ( ) )
1517
1512
}
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 > {
1524
1519
// TODO: find the destination (need to handle references, debug objects, etc.)
1525
1520
// TODO: convert object to be of the type of destination, in line with 19.3.5 of the spec
1526
1521
// TODO: write the object to the destination, including e.g. field writes that then lead to
1527
1522
// literally god knows what.
1528
1523
let object = object. unwrap_transparent_reference ( ) ;
1524
+
1529
1525
match target {
1530
1526
Argument :: Object ( target) => match target. gain_mut ( ) {
1531
1527
Object :: Integer ( target) => match object. gain_mut ( ) {
@@ -2581,6 +2577,12 @@ mod tests {
2581
2577
fn release ( & self , _mutex : Handle ) { }
2582
2578
}
2583
2579
2580
+ #[ test]
2581
+ fn verify_interpreter_send_sync ( ) {
2582
+ fn test_send_sync < T : Send + Sync > ( ) { }
2583
+ test_send_sync :: < Interpreter < TestHandler > > ( ) ;
2584
+ }
2585
+
2584
2586
#[ test]
2585
2587
fn add_op ( ) {
2586
2588
let interpreter = Interpreter :: new ( TestHandler , 2 ) ;
0 commit comments