https://github.com/akkartik/mu/blob/main/linux/133subx-widths.subx
  1 # Helpers to compute widths of SubX arguments based on their metadata.
  2 #   /imm8 => 1 byte,
  3 #   /disp32 => 4 bytes,
  4 #   and so on.
  5 
  6 == code
  7 #   instruction                     effective address                                                   register    displacement    immediate
  8 # . op          subop               mod             rm32          base        index         scale       r32
  9 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
 10 
 11 compute-width:  # word: (addr array byte) -> result/eax: int
 12     # . prologue
 13     55/push-ebp
 14     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 15     # . save registers
 16     51/push-ecx
 17     # eax = word
 18     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to ecx
 19     # var ecx: (addr byte) = &word[word->size]
 20     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 21     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
 22     # eax = word->data
 23     05/add-to-eax  4/imm32
 24     # var in/ecx: slice = {eax, ecx}
 25     51/push-ecx
 26     50/push-eax
 27     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 28     # return compute-width-of-slice(ecx)
 29     # . . push args
 30     51/push-ecx
 31     # . . call
 32     e8/call  compute-width-of-slice/disp32
 33     # . . discard args
 34     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 35 $compute-width:end:
 36     # . reclaim locals
 37     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 38     # . restore registers
 39     59/pop-to-ecx
 40     # . epilogue
 41     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 42     5d/pop-to-ebp
 43     c3/return
 44 
 45 compute-width-of-slice:  # s: (addr slice) -> result/eax: int
 46     # . prologue
 47     55/push-ebp
 48     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 49     # . save registers
 50     51/push-ecx
 51     # ecx = s
 52     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
 53     # if (has-metadata?(word, "imm32")) return 4
 54     # . eax = has-metadata?(word, "imm32")
 55     # . . push args
 56     68/push  "imm32"/imm32
 57     51/push-ecx
 58     # . . call
 59     e8/call  has-metadata?/disp32
 60     # . . discard args
 61     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 62     # . if (eax != false) return 4
 63     3d/compare-eax-and  0/imm32/false
 64     b8/copy-to-eax  4/imm32         # ZF is set, so we can overwrite eax now
 65     75/jump-if-!=  $compute-width-of-slice:end/disp8
 66     # if (has-metadata?(word, "disp32")) return 4
 67     # . eax = has-metadata?(word, "disp32")
 68     # . . push args
 69     68/push  "disp32"/imm32
 70     51/push-ecx
 71     # . . call
 72     e8/call  has-metadata?/disp32
 73     # . . discard args
 74     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 75     # . if (eax != false) return 4
 76     3d/compare-eax-and  0/imm32/false
 77     b8/copy-to-eax  4/imm32         # ZF is set, so we can overwrite eax now
 78     75/jump-if-!=  $compute-width-of-slice:end/disp8
 79     # if (has-metadata?(word, "imm16")) return 2
 80     # . eax = has-metadata?(word, "imm16")
 81     # . . push args
 82     68/push  "imm16"/imm32
 83     51/push-ecx
 84     # . . call
 85     e8/call  has-metadata?/disp32
 86     # . . discard args
 87     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 88     # . if (eax != false) return 2
 89     3d/compare-eax-and  0/imm32/false
 90     b8/copy-to-eax  2/imm32         # ZF is set, so we can overwrite eax now
 91     75/jump-if-!=  $compute-width-of-slice:end/disp8
 92     # if (has-metadata?(word, "disp16")) return 2
 93     # . eax = has-metadata?(word, "disp16")
 94     # . . push args
 95     68/push  "disp16"/imm32
 96     51/push-ecx
 97     # . . call
 98     e8/call  has-metadata?/disp32
 99     # . . discard args
100     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
101     # . if (eax != false) return 2
102     3d/compare-eax-and  0/imm32/false
103     b8/copy-to-eax  2/imm32         # ZF is set, so we can overwrite eax now
104     75/jump-if-!=  $compute-width-of-slice:end/disp8
105     # otherwise return 1
106     b8/copy-to-eax  1/imm32
107 $compute-width-of-slice:end:
108     # . restore registers
109     59/pop-to-ecx
110     # . epilogue
111     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
112     5d/pop-to-ebp
113     c3/return
114 
115 test-compute-width:
116     # . prologue
117     55/push-ebp
118     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
119 $test-compute-width:imm8:
120     # eax = compute-width("0x2/imm8")
121     # . . push args
122     68/push  "0x2/imm8"/imm32
123     # . . call
124     e8/call  compute-width/disp32
125     # . . discard args
126     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
127     # check-ints-equal(eax, 1, msg)
128     # . . push args
129     68/push  "F - test-compute-width: 0x2/imm8"/imm32
130     50/push-eax
131     68/push  1/imm32
132     # . . call
133     e8/call  check-ints-equal/disp32
134     # . . discard args
135     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
136 $test-compute-width:imm16:
137     # eax = compute-width("4/imm16")
138     # . . push args
139     68/push  "4/imm16"/imm32
140     # . . call
141     e8/call  compute-width/disp32
142     # . . discard args
143     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
144     # check-ints-equal(eax, 2, msg)
145     # . . push args
146     68/push  "F - test-compute-width: 4/imm16"/imm32
147     50/push-eax
148     68/push  2/imm32
149     # . . call
150     e8/call  check-ints-equal/disp32
151     # . . discard args
152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
153 $test-compute-width:imm32:
154     # eax = compute-width("4/imm32")
155     # . . push args
156     68/push  "4/imm32"/imm32
157     # . . call
158     e8/call  compute-width/disp32
159     # . . discard args
160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
161     # check-ints-equal(eax, 4, msg)
162     # . . push args
163     68/push  "F - test-compute-width: 4/imm32"/imm32
164     50/push-eax
165     68/push  4/imm32
166     # . . call
167     e8/call  check-ints-equal/disp32
168     # . . discard args
169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
170 $test-compute-width:disp8:
171     # eax = compute-width("foo/disp8")
172     # . . push args
173     68/push  "foo/disp8"/imm32
174     # . . call
175     e8/call  compute-width/disp32
176     # . . discard args
177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
178     # check-ints-equal(eax, 1, msg)
179     # . . push args
180     68/push  "F - test-compute-width: foo/disp8"/imm32
181     50/push-eax
182     68/push  1/imm32
183     # . . call
184     e8/call  check-ints-equal/disp32
185     # . . discard args
186     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erro