827dd4a7fe
One less error that's only in the bootstrap phase. On the other hand, for simplicity I got rid of the ability to override the Entry label. One less special case, but we're also going further from the ability to run subsets of layers. We haven't really been exercising it for a long time, though (commit 7842, March 2021 when we made baremetal the default).
264 lines
13 KiB
Plaintext
264 lines
13 KiB
Plaintext
# Comparing 'regular' size-prefixed strings.
|
|
|
|
== code
|
|
# instruction effective address register displacement immediate
|
|
# . op subop mod rm32 base index scale r32
|
|
# . 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
|
|
|
|
#? Entry: # run all tests
|
|
#? #? e8/call test-compare-equal-strings/disp32
|
|
#? e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
|
|
#? # syscall_exit(Num-test-failures)
|
|
#? 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx
|
|
#? e8/call syscall_exit/disp32
|
|
|
|
string-equal?: # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
|
|
# pseudocode:
|
|
# if (s->size != benchmark->size) return false
|
|
# return string-starts-with?(s, benchmark)
|
|
#
|
|
# . prologue
|
|
55/push-ebp
|
|
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
|
# . save registers
|
|
51/push-ecx
|
|
56/push-esi
|
|
57/push-edi
|
|
# esi = s
|
|
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
|
|
# edi = benchmark
|
|
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi
|
|
# ecx = s->size
|
|
8b/copy 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # copy *esi to ecx
|
|
$string-equal?:sizes:
|
|
# if (ecx != benchmark->size) return false
|
|
39/compare 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # compare *edi and ecx
|
|
b8/copy-to-eax 0/imm32/false
|
|
75/jump-if-!= $string-equal?:end/disp8
|
|
$string-equal?:contents:
|
|
# string-starts-with?(s, benchmark)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
|
# . . call
|
|
e8/call string-starts-with?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
$string-equal?:end:
|
|
# . restore registers
|
|
5f/pop-to-edi
|
|
5e/pop-to-esi
|
|
59/pop-to-ecx
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
string-starts-with?: # s: (addr array byte), benchmark: (addr array byte) -> result/eax: boolean
|
|
# pseudocode:
|
|
# if (s->size < benchmark->size) return false
|
|
# currs = s->data
|
|
# currb = benchmark->data
|
|
# maxb = &benchmark->data[benchmark->size]
|
|
# while currb < maxb
|
|
# c1 = *currs
|
|
# c2 = *currb
|
|
# if (c1 != c2) return false
|
|
# ++currs, ++currb
|
|
# return true
|
|
#
|
|
# registers:
|
|
# currs: esi
|
|
# maxs: ecx
|
|
# currb: edi
|
|
# c1: eax
|
|
# c2: ebx
|
|
#
|
|
# . prologue
|
|
55/push-ebp
|
|
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
|
# . save registers
|
|
51/push-ecx
|
|
52/push-edx
|
|
56/push-esi
|
|
57/push-edi
|
|
# esi = s
|
|
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi
|
|
# edi = benchmark
|
|
8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 7/r32/edi 0xc/disp8 . # copy *(ebp+12) to edi
|
|
# var bsize/ecx: int = benchmark->size
|
|
8b/copy 0/mod/indirect 7/rm32/edi . . . 1/r32/ecx . . # copy *edi to ecx
|
|
$string-starts-with?:sizes:
|
|
# if (s->size < bsize) return false
|
|
39/compare 0/mod/indirect 6/rm32/esi . . . 1/r32/ecx . . # compare *esi with ecx
|
|
7c/jump-if-< $string-starts-with?:false/disp8
|
|
# var currs/esi: (addr byte) = s->data
|
|
81 0/subop/add 3/mod/direct 6/rm32/esi . . . . . 4/imm32 # add to esi
|
|
# var currb/edi: (addr byte) = benchmark->data
|
|
81 0/subop/add 3/mod/direct 7/rm32/edi . . . . . 4/imm32 # add to edi
|
|
# var maxb/ecx: (addr byte) = &benchmark->data[benchmark->size]
|
|
01/add 3/mod/direct 1/rm32/ecx . . . 7/r32/edi . . # add edi to ecx
|
|
# var c1/eax: byte = 0
|
|
31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax
|
|
# var c2/edx: byte = 0
|
|
31/xor 3/mod/direct 2/rm32/edx . . . 2/r32/edx . . # clear edx
|
|
$string-starts-with?:loop:
|
|
# if (currs >= maxs) return true
|
|
39/compare 3/mod/direct 7/rm32/edi . . . 1/r32/ecx . . # compare edi with ecx
|
|
73/jump-if-addr>= $string-starts-with?:true/disp8
|
|
# c1 = *currs
|
|
8a/copy-byte 0/mod/indirect 6/rm32/esi . . . 0/r32/AL . . # copy byte at *esi to AL
|
|
# c2 = *currb
|
|
8a/copy-byte 0/mod/indirect 7/rm32/edi . . . 2/r32/DL . . # copy byte at *edi to DL
|
|
# if (c1 != c2) return false
|
|
39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax and edx
|
|
75/jump-if-!= $string-starts-with?:false/disp8
|
|
# ++currs
|
|
46/increment-esi
|
|
# ++currb
|
|
47/increment-edi
|
|
eb/jump $string-starts-with?:loop/disp8
|
|
$string-starts-with?:true:
|
|
b8/copy-to-eax 1/imm32
|
|
eb/jump $string-starts-with?:end/disp8
|
|
$string-starts-with?:false:
|
|
b8/copy-to-eax 0/imm32
|
|
$string-starts-with?:end:
|
|
# . restore registers
|
|
5f/pop-to-edi
|
|
5e/pop-to-esi
|
|
5a/pop-to-edx
|
|
59/pop-to-ecx
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
# - tests
|
|
|
|
test-compare-empty-with-empty-string:
|
|
# eax = string-equal?("", "")
|
|
# . . push args
|
|
68/push ""/imm32
|
|
68/push ""/imm32
|
|
# . . call
|
|
e8/call string-equal?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
# check-ints-equal(eax, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-compare-empty-with-empty-string"/imm32
|
|
68/push 1/imm32/true
|
|
50/push-eax
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
c3/return
|
|
|
|
test-compare-empty-with-non-empty-string: # also checks size-mismatch code path
|
|
# eax = string-equal?("", "Abc")
|
|
# . . push args
|
|
68/push "Abc"/imm32
|
|
68/push ""/imm32
|
|
# . . call
|
|
e8/call string-equal?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
# check-ints-equal(eax, 0, msg)
|
|
# . . push args
|
|
68/push "F - test-compare-empty-with-non-empty-string"/imm32
|
|
68/push 0/imm32/false
|
|
50/push-eax
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
c3/return
|
|
|
|
test-compare-equal-strings:
|
|
# eax = string-equal?("Abc", "Abc")
|
|
# . . push args
|
|
68/push "Abc"/imm32
|
|
68/push "Abc"/imm32
|
|
# . . call
|
|
e8/call string-equal?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
# check-ints-equal(eax, 1, msg)
|
|
# . . push args
|
|
68/push "F - test-compare-equal-strings"/imm32
|
|
68/push 1/imm32/true
|
|
50/push-eax
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
c3/return
|
|
|
|
test-compare-inequal-strings-equal-sizes:
|
|
# eax = string-equal?("Abc", "Adc")
|
|
# . . push args
|
|
68/push "Adc"/imm32
|
|
68/push "Abc"/imm32
|
|
# . . call
|
|
e8/call string-equal?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
# check-ints-equal(eax, 0, msg)
|
|
# . . push args
|
|
68/push "F - test-compare-inequal-strings-equal-sizes"/imm32
|
|
68/push 0/imm32/false
|
|
50/push-eax
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
c3/return
|
|
|
|
# helper for later tests
|
|
check-strings-equal: # s: (addr array byte), expected: (addr array byte), msg: (addr array byte)
|
|
# . prologue
|
|
55/push-ebp
|
|
89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp
|
|
# . save registers
|
|
50/push-eax
|
|
# var eax: boolean = string-equal?(s, expected)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12)
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8)
|
|
# . . call
|
|
e8/call string-equal?/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
# check-ints-equal(eax, 1, msg)
|
|
# . . push args
|
|
ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16)
|
|
68/push 1/imm32
|
|
50/push-eax
|
|
# . . call
|
|
e8/call check-ints-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp
|
|
$check-strings-equal:end:
|
|
# . restore registers
|
|
58/pop-to-eax
|
|
# . epilogue
|
|
89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp
|
|
5d/pop-to-ebp
|
|
c3/return
|
|
|
|
# test the helper
|
|
test-check-strings-equal:
|
|
# check-strings-equal("Abc", "Abc")
|
|
# . . push args
|
|
68/push "Abc"/imm32
|
|
68/push "Abc"/imm32
|
|
# . . call
|
|
e8/call check-strings-equal/disp32
|
|
# . . discard args
|
|
81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp
|
|
c3/return
|
|
|
|
# . . vim:nowrap:textwidth=0
|