Skip to content

Commit

Permalink
minor corrections from C++ implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
adrian-prantl committed Oct 30, 2024
1 parent 0b88de8 commit fc2b904
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 41 deletions.
48 changes: 30 additions & 18 deletions lldb/docs/resources/formatterbytecode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,38 +50,38 @@ Stack operations
These instructions manipulate the data stack directly.

======== ========== ===========================
Opcode Mnemonic Stack effect
Opcode Mnemonic Stack effect
-------- ---------- ---------------------------
0x00 `dup` `(x -> x x)`
0x01 `drop` `(x y -> x)`
0x02 `pick` `(x ... UInt -> x ... x)`
0x03 `over` `(x y -> x y x)`
0x04 `swap` `(x y -> y x)`
0x05 `rot` `(x y z -> z x y)`
0x00 `dup` `(x -> x x)`
0x01 `drop` `(x y -> x)`
0x02 `pick` `(x ... UInt -> x ... x)`
0x03 `over` `(x y -> x y x)`
0x04 `swap` `(x y -> y x)`
0x05 `rot` `(x y z -> z x y)`
======= ========== ===========================

Control flow
~~~~~~~~~~~~

These manipulate the control stack and program counter.
These manipulate the control stack and program counter. Both `if` and `ifelse` expect a `UInt` at the top of the data stack to represent the condition.

======== ========== ============================================================
Opcode Mnemonic Description
Opcode Mnemonic Description
-------- ---------- ------------------------------------------------------------
0x10 `{` push a code block address onto the control stack
-- `}` (technically not an opcode) syntax for end of code block
0x11 `if` pop a block from the control stack,
0x11 `if` `(UInt -> )` pop a block from the control stack,
if the top of the data stack is nonzero, execute it
0x12 `ifelse` pop two blocks from the control stack, if the top of
the data stack is nonzero, execute the first,
0x12 `ifelse` `(UInt -> )` pop two blocks from the control stack, if
the top of the data stack is nonzero, execute the first,
otherwise the second.
======== ========== ============================================================

Literals for basic types
~~~~~~~~~~~~~~~~~~~~~~~~

======== =========== ============================================================
Opcode Mnemonic Description
Opcode Mnemonic Description
-------- ----------- ------------------------------------------------------------
0x20 `123u` `( -> UInt)` push an unsigned 64-bit host integer
0x21 `123` `( -> Int)` push a signed 64-bit host integer
Expand All @@ -90,11 +90,25 @@ Literals for basic types
selectors. See `call`.
======== =========== ============================================================

Conversion operations
~~~~~~~~~~~~~~~~~~~~~

======== =========== ================================================================
Opcode Mnemonic Description
-------- ----------- ----------------------------------------------------------------
0x2a `as_int` `( UInt -> Int)` reinterpret a UInt as an Int
0x2b `as_uint` `( Int -> UInt)` reinterpret an Int as a UInt
0x2c `is_null` `( Object -> UInt )` check an object for null `(object ? 0 : 1)`
======== =========== ================================================================


Arithmetic, logic, and comparison operations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

All of these operations are only defined for `Int` and `UInt` and both operands need to be of the same type. The `>>` operator is an arithmetic shift if the parameters are of type `Int`, otherwise it's a logical shift to the right.

======== ========== ===========================
Opcode Mnemonic Stack effect
Opcode Mnemonic Stack effect
-------- ---------- ---------------------------
0x30 `+` `(x y -> [x+y])`
0x31 `-` etc ...
Expand All @@ -103,7 +117,6 @@ Arithmetic, logic, and comparison operations
0x34 `%`
0x35 `<<`
0x36 `>>`
0x37 `shra` (arithmetic shift right)
0x40 `~`
0x41 `|`
0x42 `^`
Expand All @@ -121,7 +134,7 @@ Function calls
For security reasons the list of functions callable with `call` is predefined. The supported functions are either existing methods on `SBValue`, or string formatting operations.

======== ========== ============================================
Opcode Mnemonic Stack effect
Opcode Mnemonic Stack effect
-------- ---------- --------------------------------------------
0x60 `call` `(Object argN ... arg0 Selector -> retval)`
======== ========== ============================================
Expand All @@ -144,7 +157,6 @@ Sel. Mnemonic Stack Effect
0x21 `get_value_as_unsigned` `(Object @get_value_as_unsigned -> UInt)` `SBValue::GetValueAsUnsigned`
0x22 `get_value_as_signed` `(Object @get_value_as_signed -> Int)` `SBValue::GetValueAsSigned`
0x23 `get_value_as_address` `(Object @get_value_as_address -> UInt)` `SBValue::GetValueAsAddress`
0x24 `get_value_as_address` `(Object @get_value_as_address -> UInt)` `SBValue::GetValueAsAddress`
0x40 `read_memory_byte` `(UInt @read_memory_byte -> UInt)` `Target::ReadMemory`
0x41 `read_memory_uint32` `(UInt @read_memory_uint32 -> UInt)` `Target::ReadMemory`
0x42 `read_memory_int32` `(UInt @read_memory_int32 -> Int)` `Target::ReadMemory`
Expand Down Expand Up @@ -202,7 +214,7 @@ Signature Mnemonic Stack Effect
0x02 `@get_num_children` `(Object+ -> UInt)`
0x03 `@get_child_index` `(Object+ String -> UInt)`
0x04 `@get_child_at_index` `(Object+ UInt -> Object)`
0x05 `@get_value` `(Object+ -> Object)`
0x05 `@get_value` `(Object+ -> String)`
========= ====================== ==========================

If not specified, the init function defaults to an empty function that just passes the Object along. Its results may be cached and allow common prep work to be done for an Object that can be reused by subsequent calls to the other methods. This way subsequent calls to `@get_child_at_index` can avoid recomputing shared information, for example.
Expand Down
16 changes: 11 additions & 5 deletions lldb/examples/formatter-bytecode/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,17 @@ def define_opcode(n, mnemonic, name):
define_opcode(0x22, None, "lit_string")
define_opcode(0x23, None, "lit_selector")

define_opcode(0x2a, "as_int", "as_int")
define_opcode(0x2b, "as_uint", "as_uint")
define_opcode(0x2c, "is_null", "is_null")

define_opcode(0x30, "+", "plus")
define_opcode(0x31, "-", "minus")
define_opcode(0x32, "*", "mul")
define_opcode(0x33, "/", "div")
define_opcode(0x34, "%", "mod")
define_opcode(0x35, "<<", "shl")
define_opcode(0x36, ">>", "shr")
define_opcode(0x37, "shra", "shra")

define_opcode(0x40, "&", "and")
define_opcode(0x41, "|", "or")
Expand Down Expand Up @@ -357,6 +360,11 @@ def next_byte():
length -= 1
data.append(s)

elif b == op_as_uint: pass
elif b == op_as_int: pass
elif b == op_is_null:
data.append(1 if data.pop() == None else 0)

# Arithmetic, logic, etc.
elif b == op_plus:
data.append(data.pop() + data.pop())
Expand All @@ -376,9 +384,6 @@ def next_byte():
elif b == op_shr:
y = data.pop()
data.append(data.pop() >> y)
elif b == op_shra:
y = data.pop()
data.append(data.pop() >> y) # FIXME
elif b == op_and:
data.append(data.pop() & data.pop())
elif b == op_or:
Expand Down Expand Up @@ -438,7 +443,8 @@ def next_byte():
valobj = data.pop()
data.append(valobj.Cast(sbtype))
elif sel == sel_strlen:
data.append(len(data.pop()))
s = data.pop()
data.append(len(s) if s else 0)
elif sel == sel_fmt:
fmt = data.pop()
n = count_fmt_params(fmt)
Expand Down
48 changes: 30 additions & 18 deletions lldb/examples/formatter-bytecode/test/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,27 @@ def __lldb_init_module(debugger, internal_dict):
'-x "^MyOptional<.+>$"'
)


def stringify(bytecode: bytearray) -> str:
s = ""
in_hex = False
for b in bytecode:
if ((b < 32 or b > 127 or chr(b) in ['"','`',"'"]) or
(in_hex and chr(b).lower() in
['a','b','c','d','e','f','0','1','2','3','4','5','6','7','8','9'])):
s+= r'\x' + hex(b)[2:]
in_hex = True
else:
s+=chr(b)
in_hex = False
return s

def evaluate(assembler: str, data: list):
bytecode = compile(assembler)
trace = True
if trace:
print(
"Compiled to {0} bytes of bytecode:\n0x{1}".format(
len(bytecode), bytecode.hex()
"Compiled to {0} bytes of bytecode:\n{1}".format(
len(bytecode), stringify(bytecode)
)
)
result = interpret(bytecode, [], data, False) # trace)
Expand Down Expand Up @@ -62,22 +75,21 @@ def MyOptionalSummaryProvider(valobj, internal_dict):
# return val.GetValue()
summary = ""
summary += ' dup "Storage" @get_child_with_name call' # valobj storage
summary += " dup { swap } if drop" # storage
summary += ' dup "hasVal" @get_child_with_name call' # storage
summary += " @get_value_as_unsigned call" # storage int(hasVal)
summary += ' dup 2 = { drop "<could not read MyOptional>" } {'
summary += ' 0 = { "None" } {'
summary += (
" dup @get_type call 0 @get_template_argument_type call" # storage type
)
summary += " swap" # type storage
summary += ' dup is_null ~ { swap } if drop' # storage
summary += ' dup "hasVal" @get_child_with_name call' # storage obj(hasVal)
summary += ' dup is_null { drop "<could not read MyOptional>" } {'
summary += ' @get_value_as_unsigned call' # storage int(hasVal)
summary += ' 0u = { "None" } {'
summary += ' dup @get_type call'
summary += ' 0u @get_template_argument_type call' # storage type
summary += ' swap' # type storage
summary += ' "value" @get_child_with_name call' # type value
summary += " swap @cast call" # type(value)
summary += ' dup 0 = { "None" } {'
summary += " dup @summary call { @summary call } { @get_value call } ifelse"
summary += " } ifelse"
summary += " } ifelse"
summary += " } ifelse"
summary += ' swap @cast call' # type(value)
summary += ' dup is_null { "None" } {'
summary += ' dup @summary call dup @strlen call { @get_value call } { drop } ifelse'
summary += ' } ifelse'
summary += ' } ifelse'
summary += ' } ifelse'
return evaluate(summary, [valobj])


Expand Down

0 comments on commit fc2b904

Please sign in to comment.