Bytecode Descriptions (Debugging with GDB)
Next: Using Agent Expressions, Previous: General Bytecode Design, Up: Agent Expressions [Contents][Index]
F.2 Bytecode Descriptions
Each bytecode description has the following form:
add(0x02)ab⇒a+b- Pop the top two stack items,
aandb, as integers; push their sum, as an integer.
In this example, add is the name of the bytecode, and (0x02) is the one-byte value used to encode the bytecode, in hexadecimal. The phrase “a b ⇒ a+b” shows the stack before and after the bytecode executes. Beforehand, the stack must contain at least two values, a and b; since the top of the stack is to the right, b is on the top of the stack, and a is underneath it. After execution, the bytecode will have popped a and b from the stack, and replaced them with a single value, a+b. There may be other values on the stack below those shown, but the bytecode affects only those shown.
Here is another example:
const8(0x22)n- ⇒
n - Push the 8-bit integer constant
non the stack, without sign extension.
In this example, the bytecode const8 takes an operand n directly from the bytecode stream; the operand follows the const8 bytecode itself. We write any such operands immediately after the name of the bytecode, before the colon, and describe the exact encoding of the operand in the bytecode stream in the body of the bytecode description.
For the const8 bytecode, there are no stack items given before the ⇒; this simply means that the bytecode consumes no values from the stack. If a bytecode consumes no values, or produces no values, the list on either side of the ⇒ may be empty.
If a value is written as a, b, or n, then the bytecode treats it as an integer. If a value is written is addr, then the bytecode treats it as an address.
We do not fully describe the floating point operations here; although this design can be extended in a clean way to handle floating point values, they are not of immediate interest to the customer, so we avoid describing them, to save time.
float(0x01): ⇒Prefix for floating-point bytecodes. Not implemented yet.
add(0x02):ab⇒a+bPop two integers from the stack, and push their sum, as an integer.
sub(0x03):ab⇒a-bPop two integers from the stack, subtract the top value from the next-to-top value, and push the difference.
mul(0x04):ab⇒a*bPop two integers from the stack, multiply them, and push the product on the stack. Note that, when one multiplies two
n-bit numbers yielding anothern-bit number, it is irrelevant whether the numbers are signed or not; the results are the same.div_signed(0x05):ab⇒a/bPop two signed integers from the stack; divide the next-to-top value by the top value, and push the quotient. If the divisor is zero, terminate with an error.
div_unsigned(0x06):ab⇒a/bPop two unsigned integers from the stack; divide the next-to-top value by the top value, and push the quotient. If the divisor is zero, terminate with an error.
rem_signed(0x07):ab⇒a modulo bPop two signed integers from the stack; divide the next-to-top value by the top value, and push the remainder. If the divisor is zero, terminate with an error.
rem_unsigned(0x08):ab⇒a modulo bPop two unsigned integers from the stack; divide the next-to-top value by the top value, and push the remainder. If the divisor is zero, terminate with an error.
lsh(0x09):ab⇒a<<bPop two integers from the stack; let
abe the next-to-top value, andbbe the top value. Shiftaleft bybbits, and push the result.rsh_signed(0x0a):ab⇒(signed)a>>bPop two integers from the stack; let
abe the next-to-top value, andbbe the top value. Shiftaright bybbits, inserting copies of the top bit at the high end, and push the result.rsh_unsigned(0x0b):ab⇒a>>bPop two integers from the stack; let
abe the next-to-top value, andbbe the top value. Shiftaright bybbits, inserting zero bits at the high end, and push the result.log_not(0x0e):a⇒!aPop an integer from the stack; if it is zero, push the value one; otherwise, push the value zero.
bit_and(0x0f):ab⇒a&bPop two integers from the stack, and push their bitwise
and.bit_or(0x10):ab⇒a|bPop two integers from the stack, and push their bitwise
or.bit_xor(0x11):ab⇒a^bPop two integers from the stack, and push their bitwise exclusive-
or.bit_not(0x12):a⇒~aPop an integer from the stack, and push its bitwise complement.
equal(0x13):ab⇒a=bPop two integers from the stack; if they are equal, push the value one; otherwise, push the value zero.
less_signed(0x14):ab⇒a<bPop two signed integers from the stack; if the next-to-top value is less than the top value, push the value one; otherwise, push the value zero.
less_unsigned(0x15):ab⇒a<bPop two unsigned integers from the stack; if the next-to-top value is less than the top value, push the value one; otherwise, push the value zero.
ext(0x16)n:a⇒a, sign-extended fromnbitsPop an unsigned value from the stack; treating it as an
n-bit twos-complement value, extend it to full length. This means that all bits to the left of bitn-1(where the least significant bit is bit 0) are set to the value of bitn-1. Note thatnmay be larger than or equal to the width of the stack elements of the bytecode engine; in this case, the bytecode should have no effect.The number of source bits to preserve,
n, is encoded as a single byte unsigned integer following theextbytecode.zero_ext(0x2a)n:a⇒a, zero-extended fromnbitsPop an unsigned value from the stack; zero all but the bottom
nbits.The number of source bits to preserve,
n, is encoded as a single byte unsigned integer following thezero_extbytecode.ref8(0x17):addr⇒a
ref16(0x18):addr⇒a
ref32(0x19):addr⇒a
ref64(0x1a):addr⇒aPop an address
addrfrom the stack. For bytecoderefn, fetch ann-bit value fromaddr, using the natural target endianness. Push the fetched value as an unsigned integer.Note that
addrmay not be aligned in any particular way; therefnbytecodes should operate correctly for any address.If attempting to access memory at
addrwould cause a processor exception of some sort, terminate with an error.ref_float(0x1b):addr⇒d
ref_double(0x1c):addr⇒d
ref_long_double(0x1d):addr⇒d
l_to_d(0x1e):a⇒d
d_to_l(0x1f):d⇒aNot implemented yet.
dup(0x28):a=>aaPush another copy of the stack’s top element.
swap(0x2b):ab=>baExchange the top two items on the stack.
pop(0x29):a=>Discard the top value on the stack.
pick(0x32)n:a…b=>a…baDuplicate an item from the stack and push it on the top of the stack.
n, a single byte, indicates the stack item to copy. Ifnis zero, this is the same asdup; ifnis one, it copies the item under the top item, etc. Ifnexceeds the number of items on the stack, terminate with an error.rot(0x33):abc=>cabRotate the top three items on the stack. The top item (c) becomes the third item, the next-to-top item (b) becomes the top item and the third item (a) from the top becomes the next-to-top item.
if_goto(0x20)offset:a⇒Pop an integer off the stack; if it is non-zero, branch to the given offset in the bytecode string. Otherwise, continue to the next instruction in the bytecode stream. In other words, if
ais non-zero, set thepcregister tostart+offset. Thus, an offset of zero denotes the beginning of the expression.The
offsetis stored as a sixteen-bit unsigned value, stored immediately following theif_gotobytecode. It is always stored most significant byte first, regardless of the target’s normal endianness. The offset is not guaranteed to fall at any particular alignment within the bytecode stream; thus, on machines where fetching a 16-bit on an unaligned address raises an exception, you should fetch the offset one byte at a time.goto(0x21)offset: ⇒Branch unconditionally to
offset; in other words, set thepcregister tostart+offset.The offset is stored in the same way as for the
if_gotobytecode.const8(0x22)n: ⇒n
const16(0x23)n: ⇒n
const32(0x24)n: ⇒n
const64(0x25)n: ⇒nPush the integer constant
non the stack, without sign extension. To produce a small negative value, push a small twos-complement value, and then sign-extend it using theextbytecode.The constant
nis stored in the appropriate number of bytes following theconstbbytecode. The constantnis always stored most significant byte first, regardless of the target’s normal endianness. The constant is not guaranteed to fall at any particular alignment within the bytecode stream; thus, on machines where fetching a 16-bit on an unaligned address raises an exception, you should fetchnone byte at a time.reg(0x26)n: ⇒aPush the value of register number
n, without sign extension. The registers are numbered following GDB’s conventions.The register number
nis encoded as a 16-bit unsigned integer immediately following theregbytecode. It is always stored most significant byte first, regardless of the target’s normal endianness. The register number is not guaranteed to fall at any particular alignment within the bytecode stream; thus, on machines where fetching a 16-bit on an unaligned address raises an exception, you should fetch the register number one byte at a time.getv(0x2c)n: ⇒vPush the value of trace state variable number
n, without sign extension.The variable number
nis encoded as a 16-bit unsigned integer immediately following thegetvbytecode. It is always stored most significant byte first, regardless of the target’s normal endianness. The variable number is not guaranteed to fall at any particular alignment within the bytecode stream; thus, on machines where fetching a 16-bit on an unaligned address raises an exception, you should fetch the register number one byte at a time.setv(0x2d)n:v⇒vSet trace state variable number
nto the value found on the top of the stack. The stack is unchanged, so that the value is readily available if the assignment is part of a larger expression. The handling ofnis as described forgetv.trace(0x0c):addrsize⇒Record the contents of the
sizebytes ataddrin a trace buffer, for later retrieval by GDB.trace_quick(0x0d)size:addr⇒addrRecord the contents of the
sizebytes ataddrin a trace buffer, for later retrieval by GDB.sizeis a single byte unsigned integer following thetraceopcode.This bytecode is equivalent to the sequence
dup const8 size trace, but we provide it anyway to save space in bytecode strings.trace16(0x30)size:addr⇒addrIdentical to trace_quick, except that
sizeis a 16-bit big-endian unsigned integer, not a single byte. This should probably have been namedtrace_quick16, for consistency.tracev(0x2e)n: ⇒aRecord the value of trace state variable number
nin the trace buffer. The handling ofnis as described forgetv.tracenz(0x2f)addrsize⇒Record the bytes at
addrin a trace buffer, for later retrieval by GDB. Stop at either the first zero byte, or whensizebytes have been recorded, whichever occurs first.printf(0x34)numargsstring⇒Do a formatted print, in the style of the C function
printf). The value ofnumargsis the number of arguments to expect on the stack, whilestringis the format string, prefixed with a two-byte length. The last byte of the string must be zero, and is included in the length. The format string includes escaped sequences just as it appears in C source, so for instance the format string"\t%d\n"is six characters long, and the output will consist of a tab character, a decimal number, and a newline. At the top of the stack, above the values to be printed, this bytecode will pop a “function” and “channel”. If the function is nonzero, then the target may treat it as a function and call it, passing the channel as a first argument, as with the C functionfprintf. If the function is zero, then the target may simply call a standard formatted print function of its choice. In all, this bytecode pops 2 +numargsstack elements, and pushes nothing.end(0x27): ⇒Stop executing bytecode; the result should be the top element of the stack. If the purpose of the expression was to compute an lvalue or a range of memory, then the next-to-top of the stack is the lvalue’s address, and the top of the stack is the lvalue’s size, in bytes.
Next: Using Agent Expressions, Previous: General Bytecode Design, Up: Agent Expressions [Contents][Index]