https://github.com/akkartik/mu/blob/main/301array-equal.subx

      1 # Comparing arrays of numbers.
      2
      3 == code
      4
      5 array-equal?:  # a: (addr array int), b: (addr array int) -> result/eax: boolean
      6     # pseudocode:
      7     #   asize = a->size
      8     #   if (asize != b->size) return false
      9     #   i = 0
     10     #   curra = a->data
     11     #   currb = b->data
     12     #   while i < asize
     13     #     i1 = *curra
     14     #     i2 = *currb
     15     #     if (c1 != c2) return false
     16     #     i+=4, curra+=4, currb+=4
     17     #   return true
     18     #
     19     # registers:
     20     #   i: ecx
     21     #   asize: edx
     22     #   curra: esi
     23     #   currb: edi
     24     #   i1: eax
     25     #   i2: ebx
     26     #
     27     # . prologue
     28     55/push-ebp
     29     89/<- %ebp 4/r32/esp
     30     # . save registers
     31     51/push-ecx
     32     52/push-edx
     33     53/push-ebx
     34     56/push-esi
     35     57/push-edi
     36     # esi = a
     37     8b/-> *(ebp+8) 6/r32/esi
     38     # edi = b
     39     8b/-> *(ebp+0xc) 7/r32/edi
     40     # var asize/edx: int = a->size
     41     8b/-> *esi 2/r32/edx
     42 $array-equal?:sizes:
     43     # if (asize != b->size) return false
     44     39/compare *edi 2/r32/edx
     45     75/jump-if-!= $array-equal?:false/disp8
     46     # var curra/esi: (addr byte) = a->data
     47     81 0/subop/add %esi 4/imm32
     48     # var currb/edi: (addr byte) = b->data
     49     81 0/subop/add %edi 4/imm32
     50     # var i/ecx: int = 0
     51     31/xor-with %ecx 1/r32/ecx
     52     # var vala/eax: int
     53     # var valb/ebx: int
     54 $array-equal?:loop:
     55     # if (i >= asize) return true
     56     39/compare %ecx 2/r32/edx
     57     7d/jump-if->= $array-equal?:true/disp8
     58     # var vala/eax: int = *curra
     59     8b/-> *esi 0/r32/eax
     60     # var valb/ebx: int = *currb
     61     8b/-> *edi 3/r32/ebx
     62     # if (vala != valb) return false
     63     39/compare %eax 3/r32/ebx
     64     75/jump-if-!= $array-equal?:false/disp8
     65     # i += 4
     66     81 0/subop/add %ecx 4/imm32
     67     # currs += 4
     68     81 0/subop/add %esi 4/imm32
     69     # currb += 4
     70     81 0/subop/add %edi 4/imm32
     71     eb/jump $array-equal?:loop/disp8
     72 $array-equal?:true:
     73     b8/copy-to-eax 1/imm32
     74     eb/jump $array-equal?:end/disp8
     75 $array-equal?:false:
     76     b8/copy-to-eax 0/imm32
     77 $array-equal?:end:
     78     # . restore registers
     79     5f/pop-to-edi
     80     5e/pop-to-esi
     81     5b/pop-to-ebx
     82     5a/pop-to-edx
     83     59/pop-to-ecx
     84     # . epilogue
     85     89/<- %esp 5/r32/ebp
     86     5d/pop-to-ebp
     87     c3/return
     88
     89 test-compare-empty-with-empty-array:
     90     # . prologue
     91     55/push-ebp
     92     89/<- %ebp 4/r32/esp
     93     # var ecx: (array _) = []
     94     68/push 0/imm32/size
     95     89/<- %ecx 4/r32/esp
     96     # var edx: (array _) = []
     97     68/push 0/imm32/size
     98     89/<- %edx 4/r32/esp
     99     #
    100     (array-equal? %ecx %edx)  # => eax
    101     (check-ints-equal %eax 1 "F - test-compare-empty-with-empty-array")
    102     # . epilogue
    103     89/<- %esp 5/r32/ebp
    104     5d/pop-to-ebp
    105     c3/return
    106
    107 test-compare-empty-with-non-empty-array:  # also checks size-mismatch code path
    108     # . prologue
    109     55/push-ebp
    110     89/<- %ebp 4/r32/esp
    111     # var ecx: (array int) = [1]
    112     68/push 1/imm32
    113     68/push 4/imm32/size
    114     89/<- %ecx 4/r32/esp
    115     # var edx: (array int) = []
    116     68/push 0/imm32/size
    117     89/<- %edx 4/r32/esp
    118     #
    119     (array-equal? %ecx %edx)  # => eax
    120     (check-ints-equal %eax 0 "F - test-compare-empty-with-non-empty-array")
    121     # . epilogue
    122     89/<- %esp 5/r32/ebp
    123     5d/pop-to-ebp
    124     c3/return
    125
    126 test-compare-equal-arrays:
    127     # . prologue
    128     55/push-ebp
    129     89/<- %ebp 4/r32/esp
    130     # var ecx: (array int) = [1, 2, 3]
    131     68/push 3/imm32
    132     68/push 2/imm32
    133     68/push 1/imm32
    134     68/push 0xc/imm32/size
    135     89/<- %ecx 4/r32/esp
    136     # var edx: (array int) = [1, 2, 3]
    137     68/push 3/imm32
    138     68/push 2/imm32
    139     68/push 1/imm32
    140     68/push 0xc/imm32/size
    141     89/<- %edx 4/r32/esp
    142     #
    143     (array-equal? %ecx %edx)  # => eax
    144     (check-ints-equal %eax 1 "F - test-compare-equal-arrays")
    145     # . epilogue
    146     89/<- %esp 5/r32/ebp
    147     5d/pop-to-ebp
    148     c3/return
    149
    150 test-compare-inequal-arrays-equal-sizes:
    151     # . prologue
    152     55/push-ebp
    153     89/<- %ebp 4/r32/esp
    154     # var ecx: (array int) = [1, 4, 3]
    155     68/push 3/imm32
    156     68/push 4/imm32
    157     68/push 1/imm32
    158     68/push 0xc/imm32/size
    159     89/<- %ecx 4/r32/esp
    160     # var edx: (array int) = [1, 2, 3]
    161     68/push 3/imm32
    162     68/push 2/imm32
    163     68/push 1/imm32
    164     68/push 0xc/imm32/size
    165     89/<- %edx 4/r32/esp
    166     #
    167     (array-equal? %ecx %edx)  # => eax
    168     (check-ints-equal %eax 0 "F - test-compare-inequal-arrays-equal-sizes")
    169     # . epilogue
    170     89/<- %esp 5/r32/ebp
    171     5d/pop-to-ebp
    172     c3/return
    173
    174 _parse-array-of-ints:  # ad: (addr allocation-descriptor), s: (addr array byte), out: (addr handle array int)
    175     # pseudocode
    176     #   end = &s->data[s->size]
    177     #   curr = s->data
    178     #   size = 0
    179     #   while true
    180     #     if (curr >= end) break
    181     #     curr = skip-chars-matching-in-slice(curr, end, ' ')
    182     #     if (curr >= end) break
    183     #     curr = skip-chars-not-matching-in-slice(curr, end, ' ')
    184     #     ++size
    185     #   allocate-array(ad, size*4, out)
    186     #   var slice: slice = {s->data, 0}
    187     #   curr = lookup(out)->data
    188     #   while true
    189     #     if (slice->start >= end) break
    190     #     slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
    191     #     if (slice->start >= end) break
    192     #     slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
    193     #     *curr = parse-hex-int-from-slice(slice)
    194     #     curr += 4
    195     #     slice->start = slice->end
    196     #   return result
    197     #
    198     # . prologue
    199     55/push-ebp
    200     89/<- %ebp 4/r32/esp
    201     # . save registers
    202     50/push-eax
    203     51/push-ecx
    204     52/push-edx
    205     53/push-ebx
    206     56/push-esi
    207     57/push-edi
    208     # esi = s
    209     8b/-> *(ebp+0xc) 6/r32/esi
    210     # var curr/ecx: (addr byte) = s->data
    211     8d/copy-address *(esi+4) 1/r32/ecx
    212     # var end/edx: (addr byte) = &s->data[s->size]
    213     # . edx = s->size
    214     8b/-> *esi 2/r32/edx
    215     # . edx += curr
    216     01/add-to %edx 1/r32/ecx
    217     # var size/ebx: int = 0
    218     31/xor-with %ebx 3/r32/ebx
    219 $_parse-array-of-ints:loop1:
    220     # if (curr >= end) break
    221     39/compare %ecx 2/r32/edx
    222     73/jump-if-addr>= $_parse-array-of-ints:break1/disp8
    223     # curr = skip-chars-matching-in-slice(curr, end, ' ')
    224     (skip-chars-matching-in-slice %ecx %edx 0x20)  # => eax
    225     89/<- %ecx 0/r32/eax
    226     # if (curr >= end) break
    227     39/compare %ecx 2/r32/edx
    228     73/jump-if-addr>= $_parse-array-of-ints:break1/disp8
    229     # curr = skip-chars-not-matching-in-slice(curr, end, ' ')
    230     (skip-chars-not-matching-in-slice %ecx %edx 0x20)  # => eax
    231     89/<- %ecx 0/r32/eax
    232     # size += 4
    233     81 0/subop/add %ebx 4/imm32
    234     eb/jump $_parse-array-of-ints:loop1/disp8
    235 $_parse-array-of-ints:break1:
    236     (allocate-array *(ebp+8) %ebx *(ebp+0x10))
    237 $_parse-array-of-ints:pass2:
    238     # var slice/edi: slice = {s->data, 0}
    239     68/push 0/imm32/end
    240     8d/copy-address *(esi+4) 7/r32/edi
    241     57/push-edi
    242     89/<- %edi 4/r32/esp
    243     # curr = lookup(out)->data
    244     8b/-> *(ebp+0x10) 0/r32/eax
    245     (lookup *eax *(eax+4))  # => eax
    246     8d/copy-address *(eax+4) 1/r32/ecx
    247 $_parse-array-of-ints:loop2:
    248     # if (slice->start >= end) break
    249     39/compare *edi 2/r32/edx
    250     73/jump-if-addr>= $_parse-array-of-ints:end/disp8
    251     # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
    252     (skip-chars-matching-in-slice *edi %edx 0x20)  # => eax
    253     89/<- *edi 0/r32/eax
    254     # if (slice->start >= end) break
    255     39/compare *edi 2/r32/edx
    256     73/jump-if-addr>= $_parse-array-of-ints:end/disp8
    257     # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
    258     (skip-chars-not-matching-in-slice *edi %edx 0x20)  # => eax
    259     89/<- *(edi+4) 0/r32/eax
    260     # *curr = parse-hex-int-from-slice(slice)
    261     (parse-hex-int-from-slice %edi)
    262     89/<- *ecx 0/r32/eax
    263     # curr += 4
    264     81 0/subop/add %ecx 4/imm32
    265     # slice->start = slice->end
    266     8b/-> *(edi+4) 0/r32/eax
    267     89/<- *edi 0/r32/eax
    268     eb/jump $_parse-array-of-ints:loop2/disp8
    269 $_parse-array-of-ints:end:
    270     # . reclaim locals
    271     81 0/subop/add %esp 8/imm32
    272     # . restore registers
    273     5f/pop-to-edi
    274     5e/pop-to-esi
    275     5b/pop-to-ebx
    276     5a/pop-to-edx
    277     59/pop-to-ecx
    278     58/pop-to-eax
    279     # . epilogue
    280     89/<- %esp 5/r32/ebp
    281     5d/pop-to-ebp
    282     c3/return
    283
    284 test-parse-array-of-ints:
    285     # . prologue
    286     55/push-ebp
    287     89/<- %ebp 4/r32/esp
    288     # var h/esi: (handle array int)
    289     68/push 0/imm32
    290     68/push 0/imm32
    291     89/<- %esi 4/r32/esp
    292     # var ecx: (array int) = [1, 2, 3]
    293     68/push 3/imm32
    294     68/push 2/imm32
    295     68/push 1/imm32
    296     68/push 0xc/imm32/size
    297     89/<- %ecx 4/r32/esp
    298     #
    299     (_parse-array-of-ints Heap "1 2 3" %esi)
    300     (lookup *esi *(esi+4))  # => eax
    301     (array-equal? %ecx %eax)  # => eax
    302     (check-ints-equal %eax 1 "F - test-parse-array-of-ints")
    303     # . epilogue
    304     89/<- %esp 5/r32/ebp
    305     5d/pop-to-ebp
    306     c3/return
    307
    308 test-parse-array-of-ints-empty:
    309     # - empty string = empty array
    310     # . prologue
    311     55/push-ebp
    312     89/<- %ebp 4/r32/esp
    313     # var h/esi: handle
    314     68/push 0/imm32
    315     68/push 0/imm32
    316     89/<- %esi 4/r32/esp
    317     #
    318     (_parse-array-of-ints Heap "" %esi)
    319     (lookup *esi *(esi+4))  # => eax
    320     (check-ints-equal *eax 0 "F - test-parse-array-of-ints-empty")
    321     # . epilogue
    322     89/<- %esp 5/r32/ebp
    323     5d/pop-to-ebp
    324     c3/return
    325
    326 test-parse-array-of-ints-just-whitespace:
    327     # - just whitespace = empty array
    328     # . prologue
    329     55/push-ebp
    330     89/<- %ebp 4/r32/esp
    331     # var h/esi: handle
    332     68/push 0/imm32
    333     68/push 0/imm32
    334     89/<- %esi 4/r32/esp
    335     #
    336     (_parse-array-of-ints Heap Space %esi)
    337     (lookup *esi *(esi+4))  # => eax
    338     (check-ints-equal *eax 0 "F - test-parse-array-of-ints-just-whitespace")
    339     # . epilogue
    340     89/<- %esp 5/r32/ebp
    341     5d/pop-to-ebp
    342     c3/return
    343
    344 test-parse-array-of-ints-extra-whitespace:
    345     # . prologue
    346     55/push-ebp
    347     89/<- %ebp 4/r32/esp
    348     # var h/esi: handle
    349     68/push 0/imm32
    350     68/push 0/imm32
    351     89/<- %esi 4/r32/esp
    352     # var ecx: (array int) = [1, 2, 3]
    353     68/push 3/imm32
    354     68/push 2/imm32
    355     68/push 1/imm32
    356     68/push 0xc/imm32/size
    357     89/<- %ecx 4/r32/esp
    358     #
    359     (_parse-array-of-ints Heap " 1 2  3  " %esi)
    360     (lookup *esi *(esi+4))  # => eax
    361     (array-equal? %ecx %eax)  # => eax
    362     (check-ints-equal %eax 1 "F - test-parse-array-of-ints-extra-whitespace")
    363     # . epilogue
    364     89/<- %esp 5/r32/ebp
    365     5d/pop-to-ebp
    366     c3/return
    367
    368 parse-array-of-ints:  # s: (addr array byte), out: (addr handle array int)
    369     # . prologue
    370     55/push-ebp
    371     89/<- %ebp 4/r32/esp
    372     #
    373     (_parse-array-of-ints Heap *(ebp+8) *(ebp+0xc))
    374 $parse-array-of-ints:end:
    375     # . epilogue
    376     89/<- %esp 5/r32/ebp
    377     5d/pop-to-ebp
    378     c3/return
    379
    380 # helper for later tests
    381 # compare an array with a string representation of an array literal
    382 check-array-equal:  # a: (addr array int), expected: (addr string), msg: (addr string)
    383     # . prologue
    384     55/push-ebp
    385     89/<- %ebp 4/r32/esp
    386     # . save registers
    387     50/push-eax
    388     56/push-esi
    389     # var h/esi: handle
    390     68/push 0/imm32
    391     68/push 0/imm32
    392     89/<- %esi 4/r32/esp
    393     # var b/eax: (addr array int) = parse-array-of-ints(Heap, expected)
    394     (parse-array-of-ints *(ebp+0xc) %esi)
    395     (lookup *esi *(esi+4))  # => eax
    396     #
    397     (array-equal? *(ebp+8) %eax)
    398     (check-ints-equal %eax 1 *(ebp+0x10))
    399 $check-array-equal:end:
    400     # . restore registers
    401     5e/pop-to-esi
    402     58/pop-to-eax
    403     # . epilogue
    404     89/<- %esp 5/r32/ebp
    405     5d/pop-to-ebp
    406     c3/return
    407
    408 test-check-array-equal:
    409     # . prologue
    410     55/push-ebp
    411     89/<- %ebp 4/r32/esp
    412     # var ecx: (array int) = [1, 2, 3]
    413     68/push 3/imm32
    414     68/push 2/imm32
    415     68/push 1/imm32
    416     68/push 0xc/imm32/size
    417     89/<- %ecx 4/r32/esp
    418     #
    419     (check-array-equal %ecx "1 2 3" "F - test-check-array-equal")
    420     # . epilogue
    421     89/<- %esp 5/r32/ebp
    422     5d/pop-to-ebp
    423     c3/return
    424
    425 == data
    426
    427 # length-prefixed string containing just a single space
    428 Space:  # (array byte)
    429     # size: int
    430     1/imm32
    431     # data
    432     20/space