Skip to content

Change dis output to display labels instead of offsets #112137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
2 tasks done
iritkatriel opened this issue Nov 16, 2023 · 13 comments · Fixed by #112335
Closed
2 tasks done

Change dis output to display labels instead of offsets #112137

iritkatriel opened this issue Nov 16, 2023 · 13 comments · Fixed by #112335
Assignees
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement

Comments

@iritkatriel
Copy link
Member

iritkatriel commented Nov 16, 2023

TODO:

  • test for output with offsets
  • -- instead of None for missing line number? - in another PR.

Feature or enhancement

>>> def f():
...     for i in x:
...         if y:
...           z
... 
>>> dis.dis(f)
  1           RESUME                   0

  2           LOAD_GLOBAL              0 (x)
              GET_ITER
        L1:   FOR_ITER                22 (to L3)
              STORE_FAST               0 (i)

  3           LOAD_GLOBAL              2 (y)
              TO_BOOL
              POP_JUMP_IF_TRUE         2 (to L2)
              JUMP_BACKWARD           16 (to L1)

  4     L2:   LOAD_GLOBAL              4 (z)
              POP_TOP
              JUMP_BACKWARD           24 (to L1)

  2     L3:   END_FOR
              RETURN_CONST             0 (None)

This is easier to read than the current output of:

>>> dis.dis(f)
  1           0 RESUME                   0

  2           2 LOAD_GLOBAL              0 (x)
             12 GET_ITER
        >>   14 FOR_ITER                22 (to 62)
             18 STORE_FAST               0 (i)

  3          20 LOAD_GLOBAL              2 (y)
             30 TO_BOOL
             38 POP_JUMP_IF_TRUE         2 (to 46)
             42 JUMP_BACKWARD           16 (to 14)

  4     >>   46 LOAD_GLOBAL              4 (z)
             56 POP_TOP
             58 JUMP_BACKWARD           24 (to 14)

  2     >>   62 END_FOR
             64 RETURN_CONST             0 (None)

Linked PRs

@iritkatriel iritkatriel added type-feature A feature request or enhancement stdlib Python modules in the Lib dir labels Nov 16, 2023
@iritkatriel iritkatriel self-assigned this Nov 16, 2023
@iritkatriel
Copy link
Member Author

iritkatriel commented Nov 16, 2023

More ideas:

  • Distinguish between labels which are jump targets (Jx) vs exception handlers (Hx).
  • Show the start and end of exception ranges (instead of printing the exception table).

For example:

          0           RESUME                   0

          2           LOAD_NAME                0 (z)
                      TO_BOOL
                      POP_JUMP_IF_FALSE        4 (to J1)

          3           NOP

H2
|         4           LOAD_NAME                1 (x)
|                     POP_TOP
-

                      RETURN_CONST             0 (None)

          2     J1:   RETURN_CONST             0 (None)

H4
|      None     H2:   PUSH_EXC_INFO
|
|         5           LOAD_NAME                2 (Exception)
|                     CHECK_EXC_MATCH
|                     POP_JUMP_IF_FALSE        5 (to J3)
|                     POP_TOP
|
|         6           LOAD_NAME                3 (y)
|                     POP_TOP
-

                      POP_EXCEPT
                      RETURN_CONST             0 (None)

H4
|         5     J3:   RERAISE                  0
-

       None     H4:   COPY                     3
                      POP_EXCEPT
                      RERAISE                  1

For this code:

if z:
   try:
      x
   except Exception:
      y

@markshannon
Copy link
Member

In theory a disassembler should produce "assembly code" that is a human writable form of the underlying (virtual) machine code.

I don't think I would want to add those vertical bars by hand.
Would start and end labels work? Like this:

   0           RESUME                   0

   2           LOAD_NAME                0 (z)
               TO_BOOL
               POP_JUMP_IF_FALSE        4 (to j1)

   3           NOP

   4     h1:   LOAD_NAME                1 (x)
         h2:   POP_TOP
         
               RETURN_CONST             0 (None)

   2     j1:   RETURN_CONST             0 (None)

None     h3:   PUSH_EXC_INFO

   5           LOAD_NAME                2 (Exception)
               CHECK_EXC_MATCH
               POP_JUMP_IF_FALSE        5 (to j3)
               POP_TOP

   6           LOAD_NAME                3 (y)
         h4:   POP_TOP

               POP_EXCEPT
               RETURN_CONST             0 (None)

   5     j3:   RERAISE                  0

None     h5:   COPY                     3
               POP_EXCEPT
               RERAISE                  1

ExceptionTable:
  h1 to h2 -> h3 [0]
  h3 to h4 -> h5 [1] lasti
  j3 to j3 -> h5 [1] lasti

@markshannon
Copy link
Member

Since a label can appear as a jump target and in the exception table, maybe just use l1, etc instead of j1 and h1?

Also, do we want labels to the left or right of line numbers, and do we want them on their own lines or on the same line?
If we have them on their own lines, it uses more vertical space, but less width, which is easier to read:

...
h1:
   4     LOAD_NAME                1 (x)
h2:
         POP_TOP
         RETURN_CONST             0 (None)
j1:
   2     RETURN_CONST             0 (None)
...

@iritkatriel
Copy link
Member Author

I have this now:

       0     RESUME                   0

       2     LOAD_NAME                0 (z)
             TO_BOOL
             POP_JUMP_IF_FALSE        4 (to L3)

       3     NOP

L1:
       4     LOAD_NAME                1 (x)
             POP_TOP
L2:
             RETURN_CONST             0 (None)

L3:
       2     RETURN_CONST             0 (None)

L4:
    None     PUSH_EXC_INFO

       5     LOAD_NAME                2 (Exception)
             CHECK_EXC_MATCH
             POP_JUMP_IF_FALSE        5 (to L6)
             POP_TOP

       6     LOAD_NAME                3 (y)
             POP_TOP
L5:
             POP_EXCEPT
             RETURN_CONST             0 (None)

L6:
       5     RERAISE                  0

L7:
    None     COPY                     3
             POP_EXCEPT
             RERAISE                  1
ExceptionTable:
  L1 to L2 -> L4 [0]
  L4 to L5 -> L7 [1] lasti
  L6 to L7 -> L7 [1] lasti

@iritkatriel
Copy link
Member Author

New version with labels not on their own line, and an option to include offsets:

iritkatriel@Irits-MBP cpython % cat xx.py 

if a:
    try:
        1/0
    except:
        pass


iritkatriel@Irits-MBP cpython % ./python.exe -m dis xx.py   
        0     RESUME                   0

        2     LOAD_NAME                0 (a)
              TO_BOOL
              POP_JUMP_IF_FALSE        7 (to L3)

        3     NOP

L1:     4     LOAD_CONST               0 (1)
              LOAD_CONST               1 (0)
              BINARY_OP               11 (/)
              POP_TOP
L2:           RETURN_CONST             2 (None)

L3:     2     RETURN_CONST             2 (None)

L4:  None     PUSH_EXC_INFO

        5     POP_TOP

L5:     6     POP_EXCEPT
              RETURN_CONST             2 (None)

L6:  None     COPY                     3
              POP_EXCEPT
              RERAISE                  1
ExceptionTable:
  L1 to L2 -> L4 [0]
  L4 to L5 -> L6 [1] lasti

iritkatriel@Irits-MBP cpython % ./python.exe -m dis -O xx.py
        0        0   RESUME                   0

        2        2   LOAD_NAME                0 (a)
                 4   TO_BOOL
                12   POP_JUMP_IF_FALSE        7 (to L3)

        3       16   NOP

L1:     4       18   LOAD_CONST               0 (1)
                20   LOAD_CONST               1 (0)
                22   BINARY_OP               11 (/)
                26   POP_TOP
L2:             28   RETURN_CONST             2 (None)

L3:     2       30   RETURN_CONST             2 (None)

L4:  None       32   PUSH_EXC_INFO

        5       34   POP_TOP

L5:     6       36   POP_EXCEPT
                38   RETURN_CONST             2 (None)

L6:  None       40   COPY                     3
                42   POP_EXCEPT
                44   RERAISE                  1
ExceptionTable:
  L1 to L2 -> L4 [0]
  L4 to L5 -> L6 [1] lasti

@iritkatriel
Copy link
Member Author

I think it would be nice to have -- instead of None for no line number.

@gvanrossum
Copy link
Member

I still find it jarring to see the labels to the left of the line numbers. I'm so used to line numbers being the leftmost piece of information in any presentation where they are relevant. I think this would also make it easier to guess the difference between the two numbers (line and offset).

Finally. Is it time to switch to instruction indices, from byte offsets? This would halve the numbers, and make it easier to correlate them to things actually found in variables or other debug output (e.g. lltrace).

@markshannon
Copy link
Member

markshannon commented Nov 21, 2023

I think it would be nice to have -- instead of None for no line number.

None is the value you get from code.co_lines() for those instructions.
What is the advantage of --?

Is it time to switch to instruction indices, from byte offsets?

You mean code unit offsets, not instruction offsets?
Instructions are of differing lengths. They happen to all be a multiple of 2 at the moment, but they weren't in the past and might not be in the future.

@markshannon
Copy link
Member

Perhaps we should make the default what we have now (for backwards compatibility, not because it is the best or prettiest) and allow the user to choose the format.

For example, --format=JLIA would mean "Jump-targets Lines Instruction Arguments"
The default being LOIA (Line Offset Instruction Argument)

Or is that just getting too complicated?

@iritkatriel
Copy link
Member Author

I think it would be nice to have -- instead of None for no line number.

None is the value you get from code.co_lines() for those instructions. What is the advantage of --?

I think it would be more pleasing visually in the output.

@iritkatriel
Copy link
Member Author

New version is:


iritkatriel@Irits-MBP cpython % cat xx.py

if a:
    try:
        1/0
    except:
        pass

iritkatriel@Irits-MBP cpython % ./python.exe -m dis xx.py   
   0           RESUME                   0

   2           LOAD_NAME                0 (a)
               TO_BOOL
               POP_JUMP_IF_FALSE        7 (to L3)

   3           NOP

   4   L1:     LOAD_CONST               0 (1)
               LOAD_CONST               1 (0)
               BINARY_OP               11 (/)
               POP_TOP
       L2:     RETURN_CONST             2 (None)

   2   L3:     RETURN_CONST             2 (None)

None   L4:     PUSH_EXC_INFO

   5           POP_TOP

   6   L5:     POP_EXCEPT
               RETURN_CONST             2 (None)

None   L6:     COPY                     3
               POP_EXCEPT
               RERAISE                  1
ExceptionTable:
  L1 to L2 -> L4 [0]
  L4 to L5 -> L6 [1] lasti

iritkatriel@Irits-MBP cpython % ./python.exe -m dis -O xx.py
   0          0       RESUME                   0

   2          2       LOAD_NAME                0 (a)
              4       TO_BOOL
             12       POP_JUMP_IF_FALSE        7 (to L3)

   3         16       NOP

   4   L1:   18       LOAD_CONST               0 (1)
             20       LOAD_CONST               1 (0)
             22       BINARY_OP               11 (/)
             26       POP_TOP
       L2:   28       RETURN_CONST             2 (None)

   2   L3:   30       RETURN_CONST             2 (None)

None   L4:   32       PUSH_EXC_INFO

   5         34       POP_TOP

   6   L5:   36       POP_EXCEPT
             38       RETURN_CONST             2 (None)

None   L6:   40       COPY                     3
             42       POP_EXCEPT
             44       RERAISE                  1
ExceptionTable:
  L1 to L2 -> L4 [0]
  L4 to L5 -> L6 [1] lasti

@iritkatriel
Copy link
Member Author

With -- instead of None it looks like this:

   0           RESUME                   0

   2           LOAD_NAME                0 (a)
               TO_BOOL
               POP_JUMP_IF_FALSE        7 (to L3)

   3           NOP

   4   L1:     LOAD_CONST               0 (1)
               LOAD_CONST               1 (0)
               BINARY_OP               11 (/)
               POP_TOP
       L2:     RETURN_CONST             2 (None)

   2   L3:     RETURN_CONST             2 (None)

  --   L4:     PUSH_EXC_INFO

   5           POP_TOP

   6   L5:     POP_EXCEPT
               RETURN_CONST             2 (None)

  --   L6:     COPY                     3
               POP_EXCEPT
               RERAISE                  1
ExceptionTable:
  L1 to L2 -> L4 [0]
  L4 to L5 -> L6 [1] lasti

@gvanrossum
Copy link
Member

Unsurprisingly I like the version best with '--' for unknown line numbers, labels, byte (or instr!) offsets. In fact I can't wait to use it. :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir type-feature A feature request or enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants