https://github.com/akkartik/mu/blob/main/linux/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 Entry:  # run all tests
      9 #?     e8/call test-compare-equal-strings/disp32
     10     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
     11     # syscall(exit, Num-test-failures)
     12     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
     13     e8/call  syscall_exit/disp32
     14
     15 string-equal?:  # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
     16     # pseudocode:
     17     #   if (s->size != benchmark->size) return false
     18     #   return string-starts-with?(s, benchmark)
     19     #
     20     # . prologue
     21     55/push-ebp
     22     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     23     # . save registers
     24     51/push-ecx
     25     56/push-esi
     26     57/push-edi
     27     # esi = s
     28     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
     29     # edi = benchmark
     30     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
     31     # ecx = s->size
     32     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
     33 $string-equal?:sizes:
     34     # if (ecx != benchmark->size) return false
     35     39/compare                      0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # compare *edi and ecx
     36     b8/copy-to-eax  0/imm32/false
     37     75/jump-if-!=  $string-equal?:end/disp8
     38 $string-equal?:contents:
     39     # string-starts-with?(s, benchmark)
     40     # . . push args
     41     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     42     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     43     # . . call
     44     e8/call  string-starts-with?/disp32
     45     # . . discard args
     46     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     47 $string-equal?:end:
     48     # . restore registers
     49     5f/pop-to-edi
     50     5e/pop-to-esi
     51     59/pop-to-ecx
     52     # . epilogue
     53     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     54     5d/pop-to-ebp
     55     c3/return
     56
     57 string-starts-with?:  # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
     58     # pseudocode:
     59     #   if (s->size < benchmark->size) return false
     60     #   currs = s->data
     61     #   currb = benchmark->data
     62     #   maxb = &benchmark->data[benchmark->size]
     63     #   while currb < maxb
     64     #     c1 = *currs
     65     #     c2 = *currb
     66     #     if (c1 != c2) return false
     67     #     ++currs, ++currb
     68     #   return true
     69     #
     70     # registers:
     71     #   currs: esi
     72     #   maxs: ecx
     73     #   currb: edi
     74     #   c1: eax
     75     #   c2: ebx
     76     #
     77     # . prologue
     78     55/push-ebp
     79     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     80     # . save registers
     81     51/push-ecx
     82     52/push-edx
     83     56/push-esi
     84     57/push-edi
     85     # esi = s
     86     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
     87     # edi = benchmark
     88     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
     89     # var bsize/ecx: int = benchmark->size
     90     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
     91 $string-starts-with?:sizes:
     92     # if (s->size < bsize) return false
     93     39/compare                      0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare *esi with ecx
     94     7c/jump-if-<  $string-starts-with?:false/disp8
     95     # var currs/esi: (addr byte) = s->data
     96     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               4/imm32           # add to esi
     97     # var currb/edi: (addr byte) = benchmark->data
     98     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm32           # add to edi
     99     # var maxb/ecx: (addr byte) = &benchmark->data[benchmark->size]
    100     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           7/r32/edi   .               .                 # add edi to ecx
    101     # var c1/eax: byte = 0
    102     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    103     # var c2/edx: byte = 0
    104     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
    105 $string-starts-with?:loop:
    106     # if (currs >= maxs) return true
    107     39/compare                      3/mod/direct    7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # compare edi with ecx
    108     73/jump-if-addr>=  $string-starts-with?:true/disp8
    109     # c1 = *currs
    110     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
    111     # c2 = *currb
    112     8a/copy-byte                    0/mod/indirect  7/rm32/edi    .           .             .           2/r32/DL    .               .                 # copy byte at *edi to DL
    113     # if (c1 != c2) return false
    114     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax and edx
    115     75/jump-if-!=  $string-starts-with?:false/disp8
    116     # ++currs
    117     46/increment-esi
    118     # ++currb
    119     47/increment-edi
    120     eb/jump  $string-starts-with?:loop/disp8
    121 $string-starts-with?:true:
    122     b8/copy-to-eax  1/imm32
    123     eb/jump  $string-starts-with?:end/disp8
    124 $string-starts-with?:false:
    125     b8/copy-to-eax  0/imm32
    126 $string-starts-with?:end:
    127     # . restore registers
    128     5f/pop-to-edi
    129     5e/pop-to-esi
    130     5a/pop-to-edx
    131     59/pop-to-ecx
    132     # . epilogue
    133     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    134     5d/pop-to-ebp
    135     c3/return
    136
    137 # - tests
    138
    139 test-compare-empty-with-empty-string:
    140     # eax = string-equal?("", "")
    141     # . . push args
    142     68/push  ""/imm32
    143     68/push  ""/imm32
    144     # . . call
    145     e8/call  string-equal?/disp32
    146     # . . discard args
    147     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    148     # check-ints-equal(eax, 1, msg)
    149     # . . push args
    150     68/push  "F - test-compare-empty-with-empty-string"/imm32
    151     68/push  1/imm32/true
    152     50/push-eax
    153     # . . call
    154     e8/call  check-ints-equal/disp32
    155     # . . discard args
    156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    157     c3/return
    158
    159 test-compare-empty-with-non-empty-string:  # also checks size-mismatch code path
    160     # eax = string-equal?("", "Abc")
    161     # . . push args
    162     68/push  "Abc"/imm32
    163     68/push  ""/imm32
    164     # . . call
    165     e8/call  string-equal?/disp32
    166     # . . discard args
    167     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    168     # check-ints-equal(eax, 0, msg)
    169     # . . push args
    170     68/push  "F - test-compare-empty-with-non-empty-string"/imm32
    171     68/push  0/imm32/false
    172     50/push-eax
    173     # . . call
    174     e8/call  check-ints-equal/disp32
    175     # . . discard args
    176     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    177     c3/return
    178
    179 test-compare-equal-strings:
    180     # eax = string-equal?("Abc", "Abc")
    181     # . . push args
    182     68/push  "Abc"/imm32
    183     68/push  "Abc"/imm32
    184     # . . call
    185     e8/call  string-equal?/disp32
    186     # . . discard args
    187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    188     # check-ints-equal(eax, 1, msg)
    189     # . . push args
    190     68/push  "F - test-compare-equal-strings"/imm32
    191     68/push  1/imm32/true
    192     50/push-eax
    193     # . . call
    194     e8/call  check-ints-equal/disp32
    195     # . . discard args
    196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    197     c3/return
    198
    199 test-compare-inequal-strings-equal-sizes:
    200     # eax = string-equal?("Abc", "Adc")
    201     # . . push args
    202     68/push  "Adc"/imm32
    203     68/push  "Abc"/imm32
    204     # . . call
    205     e8/call  string-equal?/disp32
    206     # . . discard args
    207     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    208     # check-ints-equal(eax, 0, msg)
    209     # . . push args
    210     68/push  "F - test-compare-inequal-strings-equal-sizes"/imm32
    211     68/push  0/imm32/false
    212     50/push-eax
    213     # . . call
    214     e8/call  check-ints-equal/disp32
    215     # . . discard args
    216     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    217     c3/return
    218
    219 # helper for later tests
    220 check-strings-equal:  # s: (addr array byte), expected: (addr array byte), msg: (addr array byte)
    221     # . prologue
    222     55/push-ebp
    223     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    224     # . save registers
    225     50/push-eax
    226     # var eax: boolean = string-equal?(s, expected)
    227     # . . push args
    228     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    229     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    230     # . . call
    231     e8/call  string-equal?/disp32
    232     # . . discard args
    233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    234     # check-ints-equal(eax, 1, msg)
    235     # . . push args
    236     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    237     68/push  1/imm32
    238     50/push-eax
    239     # . . call
    240     e8/call  check-ints-equal/disp32
    241     # . . discard args
    242     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    243 $check-strings-equal:end:
    244     # . restore registers
    245     58/pop-to-eax
    246     # . epilogue
    247     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    248     5d/pop-to-ebp
    249     c3/return
    250
    251 # test the helper
    252 test-check-strings-equal:
    253     # check-strings-equal("Abc", "Abc")
    254     # . . push args
    255     68/push  "Abc"/imm32
    256     68/push  "Abc"/imm32
    257     # . . call
    258     e8/call  check-strings-equal/disp32
    259     # . . discard args
    260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    261     c3/return
    262
    263 # . . vim:nowrap:textwidth=0