Invokes interpreted code. The `bytecode` pointer should previously have been produced by Cranelift and `callee` / `caller` / `args_and_results` are normal array-call arguments being passed around.
(
mut self,
mut bytecode: NonNull<u8>,
callee: NonNull<VMOpaqueContext>,
caller: NonNull<VMContext>,
args_and_results: NonNull<[ValRaw]>,
)
| 166 | /// and `callee` / `caller` / `args_and_results` are normal array-call |
| 167 | /// arguments being passed around. |
| 168 | pub unsafe fn call( |
| 169 | mut self, |
| 170 | mut bytecode: NonNull<u8>, |
| 171 | callee: NonNull<VMOpaqueContext>, |
| 172 | caller: NonNull<VMContext>, |
| 173 | args_and_results: NonNull<[ValRaw]>, |
| 174 | ) -> bool { |
| 175 | // Initialize argument registers with the ABI arguments. |
| 176 | let args = [ |
| 177 | XRegVal::new_ptr(callee.as_ptr()).into(), |
| 178 | XRegVal::new_ptr(caller.as_ptr()).into(), |
| 179 | XRegVal::new_ptr(args_and_results.cast::<u8>().as_ptr()).into(), |
| 180 | XRegVal::new_u64(args_and_results.len() as u64).into(), |
| 181 | ]; |
| 182 | |
| 183 | let mut vm = self.vm(); |
| 184 | |
| 185 | let old_lr = unsafe { vm.call_start(&args) }; |
| 186 | |
| 187 | // Run the interpreter as much as possible until it finishes, and then |
| 188 | // handle each finish condition differently. |
| 189 | let ret = loop { |
| 190 | match unsafe { vm.call_run(bytecode) } { |
| 191 | // If the VM returned entirely then read the return value and |
| 192 | // return that (it indicates whether a trap happened or not. |
| 193 | DoneReason::ReturnToHost(()) => { |
| 194 | match unsafe { vm.call_end(old_lr, [RegType::XReg]).next().unwrap() } { |
| 195 | #[allow( |
| 196 | clippy::cast_possible_truncation, |
| 197 | reason = "intentionally reading the lower bits only" |
| 198 | )] |
| 199 | Val::XReg(xreg) => break (xreg.get_u32() as u8) != 0, |
| 200 | _ => unreachable!(), |
| 201 | } |
| 202 | } |
| 203 | // If the VM wants to call out to the host then dispatch that |
| 204 | // here based on `id`. Once that returns we typically resume |
| 205 | // execution at `resume`. |
| 206 | DoneReason::CallIndirectHost { id, resume } => { |
| 207 | unsafe { |
| 208 | self.call_indirect_host(id); |
| 209 | } |
| 210 | |
| 211 | // After the host has finished take a look at what hostcall |
| 212 | // was just made. The `raise` hostcall gets special handling |
| 213 | // for its non-local transfer of control flow. |
| 214 | // |
| 215 | // Also note that for non-`raise` hostcalls the |
| 216 | // `state.resume_at_pc` value should always be `None`. |
| 217 | if u32::from(id) == HostCall::Builtin(BuiltinFunctionIndex::raise()).index() { |
| 218 | bytecode = self.take_resume_at_pc(); |
| 219 | } else { |
| 220 | debug_assert!(self.vm_state().resume_at_pc.is_none()); |
| 221 | bytecode = resume; |
| 222 | } |
| 223 | vm = self.vm(); |
| 224 | } |
| 225 | // If the VM trapped then process that here and return `false`. |
no test coverage detected