https://github.com/akkartik/mu/blob/main/105string-equal.subx
  1 # Comparing 'regular' size-prefixed strings.
  2 
  3 == code
  4 #   instruction                     effective address                                                   register    displacement    immediate
  5 # . op          subop               mod             rm32          base        index         scale       r32
  6 # . 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
  7 
  8 string-equal?:  # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
  9     # pseudocode:
 10     #   if (s->size != benchmark->size) return false
 11     #   return string-starts-with?(s, benchmark)
 12     #
 13     # . prologue
 14     55/push-ebp
 15     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 16     # . save registers
 17     51/push-ecx
 18     56/push-esi
 19     57/push-edi
 20     # esi = s
 21     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 22     # edi = benchmark
 23     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
 24     # ecx = s->size
 25     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
 26 $string-equal?:sizes:
 27     # if (ecx != benchmark->size) return false
 28     39/compare                      0/mod/indirect  7/rm32/edi    .           .             .           
c{0: 0 (((1 integer)) <- ((copy)) ((34 literal))) -- nil
c{0: 1 (((2 integer)) <- ((copy)) ((35 literal))) -- nil
c{0: 2 (((3 integer)) <- ((copy)) ((36 literal))) -- nil
c{0: 3 (((4 integer-point-pair-address)) <- ((copy)) ((1 literal))) -- nil
c{0: 4 (((5 integer-point-pair-address-address)) <- ((copy)) ((4 literal))) -- nil
c{0: 5 (((6 integer-integer-pair)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((1 offset))) -- nil
c{0: 6 (((8 integer)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((0 offset))) -- nil
c{1: 0 ✓ (((1 integer)) <- ((copy)) ((34 literal)))
c{1: 1 ✓ (((2 integer)) <- ((copy)) ((35 literal)))
c{1: 2 ✓ (((3 integer)) <- ((copy)) ((36 literal)))
c{1: 3 ✓ (((4 integer-point-pair-address)) <- ((copy)) ((1 literal)))
c{1: 4 ✓ (((5 integer-point-pair-address-address)) <- ((copy)) ((4 literal)))
c{1: 5 ✓ (((6 integer-integer-pair)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((1 offset)))
c{1: 6 ✓ (((8 integer)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((0 offset)))
cn0: convert-names in main
cn0: (((1 integer)) <- ((copy)) ((34 literal))) nil nil
cn0: checking arg ((34 literal))
cn0: checking oarg ((1 integer))
maybe-add: ((1 integer))
cn0: (((2 integer)) <- ((copy)) ((35 literal))) nil nil
cn0: checking arg ((35 literal))
cn0: checking oarg ((2 integer))
maybe-add: ((2 integer))
cn0: (((3 integer)) <- ((copy)) ((36 literal))) nil nil
cn0: checking arg ((36 literal))
cn0: checking oarg ((3 integer))
maybe-add: ((3 integer))
cn0: (((4 integer-point-pair-address)) <- ((copy)) ((1 literal))) nil nil
cn0: checking arg ((1 literal))
cn0: checking oarg ((4 integer-point-pair-address))
maybe-add: ((4 integer-point-pair-address))
cn0: (((5 integer-point-pair-address-address)) <- ((copy)) ((4 literal))) nil nil
cn0: checking arg ((4 literal))
cn0: checking oarg ((5 integer-point-pair-address-address))
maybe-add: ((5 integer-point-pair-address-address))
cn0: (((6 integer-integer-pair)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((1 offset))) nil nil
cn0: field-access 1 in 5integer-point-pair-address-addressderefderef of type integer-point-pair
cn0: checking arg ((5 integer-point-pair-address-address) (deref) (deref))
maybe-add: ((5 integer-point-pair-address-address) (deref) (deref))
cn0: checking arg ((1 offset))
cn0: checking oarg ((6 integer-integer-pair))
maybe-add: ((6 integer-integer-pair))
cn0: (((8 integer)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((0 offset))) nil nil
cn0: field-access 0 in 5integer-point-pair-address-addressderefderef of type integer-point-pair
cn0: checking arg ((5 integer-point-pair-address-address) (deref) (deref))
maybe-add: ((5 integer-point-pair-address-address) (deref) (deref))
cn0: checking arg ((0 offset))
cn0: checking oarg ((8 integer))
maybe-add: ((8 integer))
cn1: (((1 integer)) <- ((copy)) ((34 literal)))
cn1: (((2 integer)) <- ((copy)) ((35 literal)))
cn1: (((3 integer)) <- ((copy)) ((36 literal)))
cn1: (((4 integer-point-pair-address)) <- ((copy)) ((1 literal)))
cn1: (((5 integer-point-pair-address-address)) <- ((copy)) ((4 literal)))
cn1: (((6 integer-integer-pair)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((1 offset)))
cn1: (((8 integer)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((0 offset)))
schedule: main
run: main 0: (((1 integer)) <- ((copy)) ((34 literal)))
run: main 0: 34 => ((1 integer))
mem: ((1 integer)): 1 <= 34
run: main 1: (((2 integer)) <- ((copy)) ((35 literal)))
run: main 1: 35 => ((2 integer))
mem: ((2 integer)): 2 <= 35
run: main 2: (((3 integer)) <- ((copy)) ((36 literal)))
run: main 2: 36 => ((3 integer))
mem: ((3 integer)): 3 <= 36
run: main 3: (((4 integer-point-pair-address)) <- ((copy)) ((1 literal)))
run: main 3: 1 => ((4 integer-point-pair-address))
mem: ((4 integer-point-pair-address)): 4 <= 1
run: main 4: (((5 integer-point-pair-address-address)) <- ((copy)) ((4 literal)))
run: main 4: 4 => ((5 integer-point-pair-address-address))
mem: ((5 integer-point-pair-address-address)): 5 <= 4
run: main 5: (((6 integer-integer-pair)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((1 offset)))
mem: ((2 integer-integer-pair) (raw)) => #(tagged record (35 36 . nil))
run: main 5: #(tagged record (35 36 . nil)) => ((6 integer-integer-pair))
mem: ((6 integer-integer-pair)): 6 <= 35
mem: ((6 integer-integer-pair)): 7 <= 36
run: main 6: (((8 integer)) <- ((get)) ((5 integer-point-pair-address-address) (deref) (deref)) ((0 offset)))
mem: ((1 integer) (raw)) => 34
run: main 6: 34 => ((8 integer))
mem: ((8 integer)): 8 <= 34
schedule: done with routine nil
class="subxComment"># clear edx 98 $string-starts-with?:loop: 99 # if (currs >= maxs) return true 100 39/compare 3/mod/direct 7/rm32/edi . . . 1/r32/ecx . . # compare edi with ecx 101 73/jump-if-addr>= $string-starts-with?:true/disp8 102 # c1 = *currs 103 8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL 104 # c2 = *currb 105 8a/copy-byte 0/mod/indirect 7/rm32/edi . . . 2/r32/DL . . # copy byte at *edi to DL 106 # if (c1 != c2) return false 107 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax and edx 108 75/jump-if-!= $string-starts-with?:false/disp8 109 # ++currs 110 46/increment-esi 111 # ++currb 112 47/increment-edi 113 eb/jump $string-starts-with?:loop/disp8 114 $string-starts-with?:true: 115 b8/copy-to-eax 1/imm32 116 eb/jump $string-starts-with?:end/disp8 117 $string-starts-with?:false: 118 b8/copy-to-eax 0/imm32 119 $string-starts-with?:end: 120 # . restore registers 121 5f/pop-to-edi 122 5e/pop-to-esi 123 5a/pop-to-edx 124 59/pop-to-ecx 125 # . epilogue 126 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 127 5d/pop-to-ebp 128 c3/return 129 130 # - tests 131 132 test-compare-empty-with-empty-string: 133 # eax = string-equal?("", "") 134 # . . push args 135 68/push ""/imm32 136 68/push ""/imm32 137 # . . call 138 e8/call string-equal?/disp32 139 # . . discard args 140 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 141 # check-ints-equal(eax, 1, msg) 142 # . . push args 143 68/push "F - test-compare-empty-with-empty-string"/imm32 144 68/push 1/imm32/true 145 50/push-eax 146 # . . call 147 e8/call check-ints-equal/disp32 148 # . . discard args 149 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 150 c3/return 151 152 test-compare-empty-with-non-empty-string: # also checks size-mismatch code path 153 # eax = string-equal?("", "Abc") 154 # . . push args 155 68/push "Abc"/imm32 156 68/push ""/imm32 157 # . . call 158 e8/call string-equal?/disp32 159 # . . discard args 160 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 161 # check-ints-equal(eax, 0, msg) 162 # . . push args 163 68/push "F - test-compare-empty-with-non-empty-string"/imm32 164 68/push 0/imm32/false 165 50/push-eax 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 c3/return 171 172 test-compare-equal-strings: 173 # eax = string-equal?("Abc", "Abc") 174 # . . push args 175 68/push "Abc"/imm32 176 68/push "Abc"/imm32 177 # . . call 178 e8/call string-equal?/disp32 179 # . . discard args 180 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 181 # check-ints-equal(eax, 1, msg) 182 # . . push args 183 68/push "F - test-compare-equal-strings"/imm32 184 68/push 1/imm32/true 185 50/push-eax 186 # . . call 187 e8/call check-ints-equal/disp32 188 # . . discard args 189 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 190 c3/return 191 192 test-compare-inequal-strings-equal-sizes: 193 # eax = string-equal?("Abc", "Adc") 194 # . . push args 195 68/push "Adc"/imm32 196 68/push "Abc"/imm32 197 # . . call 198 e8/call string-equal?/disp32 199 # . . discard args 200 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 201 # check-ints-equal(eax, 0, msg) 202 # . . push args 203 68/push "F - test-compare-inequal-strings-equal-sizes"/imm32 204 68/push 0/imm32/false 205 50/push-eax 206 # . . call 207 e8/call check-ints-equal/disp32 208 # . . discard args 209 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 210 c3/return 211 212 # helper for later tests 213 check-strings-equal: # s: (addr array byte), expected: (addr array byte), msg: (addr array byte) 214 # . prologue 215 55/push-ebp 216 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 217 # . save registers 218 50/push-eax 219 # var eax: boolean = string-equal?(s, expected) 220 # . . push args 221 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 222 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 223 # . . call 224 e8/call string-equal?/disp32 225 # . . discard args 226 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 227 # check-ints-equal(eax, 1, msg) 228 # . . push args 229 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) 230 68/push 1/imm32 231 50/push-eax 232 # . . call 233 e8/call check-ints-equal/disp32 234 # . . discard args 235 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 236 $check-strings-equal:end: 237 # . restore registers 238 58/pop-to-eax 239 # . epilogue 240 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 241 5d/pop-to-ebp 242 c3/return 243 244 # test the helper 245 test-check-strings-equal: 246 # check-strings-equal("Abc", "Abc") 247 # . . push args 248 68/push "Abc"/imm32 249 68/push "Abc"/imm32 250 # . . call 251 e8/call check-strings-equal/disp32 252 # . . discard args 253 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 254 c3/return 255 256 # . . vim:nowrap:textwidth=0