blob: 208e47bf7033a5cb86141c8a3ab7bf4815902e9d [file] [log] [blame]
*
* Copyright 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
Introduction
This is the documentation of the stack based virtual machine implemented in Interpreter.h and in
Interpreter.cpp for replaying arbitrary opcode streams generated by the server.
Base types
* int8_t, int16_t, int32_t, int64_t
* uint8_t, uint16_t, unit32_t, uint64_t
* float, double
* absolute pointer, volatile pointer, constant pointer
Pointers
The interpreter and the underlying stack support 3 different types of pointer. The absolute
pointer is a regular (untyped) pointer pointing to an arbitrary memory location or a null
pointer. The volatile pointer is an offset in the volatile memory and the constant pointer is an
offset inside the constant memory.
Stack
A standard LIFO stack where each element is a type (one of the base types), value pair. The
implementation of the type storage is implementation dependent (can be on a different stack).
The size of the stored elements are unified to the size of the largest storable type and all of
the elements are aligned.
Each operation, except for CLONE, consumes the operands from the current stack and pushes the
result back to the stack.
Opcodes
Each opcode is 32 bits long where the first 6 bits are the instruction code and the rest of the
bits contain the instruction data. This leaves room for additional instructions to be added in
the future.
Notation: <field_name:field_size_in_bits>
Function call
<code:6> <padding:1> <push-return:1> <padding:8> <function id:16>
* CALL(push-return, function) [-{arg-count} (any type) / +{push-return} (any type)]
* Call the specified function and if push-return is 1 then save the return value to the
stack; otherwise discard the return value
* The arguments are popped from the stack and they are type checked with the arguments
of the called function
* The arguments have to be pushed onto the stack in order (the last argument is on the
top of the stack)
* Function IDs in range 0xff00-0xffff are reserved for the implementation (callbacks)
Push
<code:6> <type:6> <data:20>
* PUSH_I(type, data) [+1 (type)]
* Push the data to the stack specified inside the opcode
* If the data type is an integer or a pointer type, then the data is copied into the
least-significant-bits of the target word, sign-extending if the type is signed.
* If the data type is a float or double, then the value is written to the sign and
exponent bits of the floating point number, and the fractional bits are set to 0.
<code:6> <type:6> <constant-address:20>
* LOAD_C(type, address) [+1 (type)]
* Push data from the given constant memory address to the stack
<code:6> <type:6> <volatile-address:20>
* LOAD_V(type, address) [+1 (type)]
* Push data from the given volatile memory address to the stack
<code:6> <type:6> <padding:20>
* LOAD(type) [-1 (pointer) / +1 (type)]
* Pop a memory address from the top of the stack and push the data at that address to
the top of the stack
Pop
<code:6> <count:26>
* POP(count) [-{count} (any type)]
* Pop and discard count values from the top of the stack
<code:6> <volatile-address:26>
* STORE_V(volatile-address) [-1 (any type)]
* Pop the top value from the the stack and save it to the given volatile memory address.
All pointer values, regardless of the pointer type on the stack, will be stored as an
absolute pointer address.
<code:6> <padding:26>
* STORE() [-2 (pointer, any type)]
* Pop the target address and then the value from the top of the stack, and then store
the value to the target address.
All pointer values, regardless of the pointer type on the stack, will be stored as an
absolute pointer address.
Load resource
<code:6> <resource-id:26>
* RESOURCE(resource-id) [-1 (pointer)]
* Pop the volatile-address from the top of the stack and loads the resource with the
given id to that address
Post data
<code:6> <padding:26>
* POST() [-2 (uint32_t, pointer)]
* Pop size and a pointer from the top of the stack and post size bytes of data from
the address to the server
Copy
<code:6> <count:26>
* COPY(count) [-2 (pointer, pointer)]
* Pop the source address then the target address from the top of the stack, and then
copy count bytes from source to target.
<code:6> <n:26>
* CLONE(n) [+1 (any type)]
* Copy the n-th element from the stack to the top of the stack
<code:6> <max-count:26>
STRCPY() [-2 (pointer, pointer)]
* Pop the source address then the target address from the top of the stack, and
then copy at most max-count minus one bytes from source to target. If the
max-count is greater than the source string length, then the target will be padded
with 0s. The destination buffer will always be 0-terminated.
Calculation
<code:6> <value:26>
* EXTEND(value) [no change]
* Extend the value at the top of the stack with the given data, in-place.
* If the data type of the top of the stack is an integer or a pointer type, then the
value on the stack is left-shifted by 26 bits and is bitwise-OR’ed with the
specified value.
* If the data type is a float or double, then the fractional part of the floating
point value on the stack is left-shifted by 26 bits and is bitwise-OR’ed with the
specified value. Bits shifted beyond the fractional part of the floating point
number are discarded.