From 4a4a392dc7c81b301ad6b760525c5549f2f6644c Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 20 Sep 2019 11:19:30 -0700 Subject: 5683 --- html/069allocate.subx.html | 409 ++++++++++++++++++++++++--------------------- 1 file changed, 223 insertions(+), 186 deletions(-) (limited to 'html/069allocate.subx.html') diff --git a/html/069allocate.subx.html b/html/069allocate.subx.html index de61f2e6..0a167fe1 100644 --- a/html/069allocate.subx.html +++ b/html/069allocate.subx.html @@ -16,9 +16,11 @@ a { color:inherit; } * { font-size:12pt; font-size: 1em; } .subxComment { color: #005faf; } .subxS2Comment { color: #8a8a8a; } +.subxFunction { color: #af5f00; text-decoration: underline; } .LineNr { } .subxS1Comment { color: #0000af; } -.subxFunction { color: #af5f00; text-decoration: underline; } +.CommentedCode { color: #8a8a8a; } +.SpecialChar { color: #d70000; } .Normal { color: #000000; background-color: #c6c6c6; padding-bottom: 1px; } .subxMinorFunction { color: #875f5f; } .Constant { color: #008787; } @@ -76,192 +78,227 @@ if ('onhashchange' in window) { 16 # very same 'allocate' helper. They just need a new allocation descriptor for 17 # their book-keeping. 18 - 19 == code - 20 # instruction effective address register displacement immediate - 21 # . op subop mod rm32 base index scale r32 - 22 # . 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 - 23 - 24 # Claim the next 'n' bytes of memory starting at ad->curr and update ad->curr. - 25 # Abort if there isn't enough memory in 'ad'. - 26 allocate: # ad : (address allocation-descriptor), n : int -> address-or-null/eax - 27 # . prolog - 28 55/push-ebp - 29 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - 30 # . save registers - 31 51/push-ecx - 32 52/push-edx - 33 # ecx = ad - 34 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx - 35 # save ad->curr - 36 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax - 37 # check if there's enough space - 38 # . edx = ad->curr + n - 39 89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx - 40 03/add 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # add *(ebp+12) to edx - 41 3b/compare 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # compare edx with *(ecx+4) - 42 73/jump-if-greater-or-equal-signed $allocate:abort/disp8 - 43 $allocate:commit: - 44 # update ad->curr - 45 89/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy edx to *ecx - 46 $allocate:end: - 47 # . restore registers - 48 5a/pop-to-edx - 49 59/pop-to-ecx - 50 # . epilog - 51 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp - 52 5d/pop-to-ebp - 53 c3/return - 54 - 55 $allocate:abort: - 56 # . _write(2/stderr, error) - 57 # . . push args - 58 68/push "allocate: failed to allocate\n"/imm32 - 59 68/push 2/imm32/stderr - 60 # . . call - 61 e8/call _write/disp32 - 62 # . . discard args - 63 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - 64 # . syscall(exit, 1) - 65 bb/copy-to-ebx 1/imm32 - 66 b8/copy-to-eax 1/imm32/exit - 67 cd/syscall 0x80/imm8 - 68 # never gets here - 69 - 70 test-allocate-success: - 71 # . prolog - 72 55/push-ebp - 73 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp - 74 # var ad/ecx : (address allocation-descriptor) = {11, 15} - 75 68/push 0xf/imm32/limit - 76 68/push 0xb/imm32/curr - 77 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx - 78 # eax = allocate(ad, 3) - 79 # . . push args - 80 68/push 3/imm32 - 81 51/push-ecx - 82 # . . call - 83 e8/call allocate/disp32 - 84 # . . discard args - 85 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp - 86 # check-ints-equal(eax, 11, msg) - 87 # . . push args - 88 68/push "F - test-allocate-success: returns current pointer of allocation descriptor"/imm32 - 89 68/push 0xb/imm32 - 90 50/push-eax - 91 # . . call - 92 e8/call check-ints-equal/disp32 - 93 # . . discard args - 94 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp - 95 # check-ints-equal(ad->curr, 14, msg) - 96 # . . push args - 97 68/push "F - test-allocate-success: updates allocation descriptor"/imm32 - 98 68/push 0xe/imm32 - 99 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx -100 # . . call -101 e8/call check-ints-equal/disp32 -102 # . . discard args -103 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -104 # . epilog -105 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp -106 5d/pop-to-ebp -107 c3/return -108 -109 _pending-test-allocate-failure: -110 # . prolog -111 55/push-ebp -112 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp -113 # var ad/ecx : (address allocation-descriptor) = {11, 15} -114 68/push 0xf/imm32/limit -115 68/push 0xb/imm32/curr -116 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx -117 # eax = allocate(ad, 6) -118 # . . push args -119 68/push 6/imm32 -120 51/push-ecx -121 # . . call -122 e8/call allocate/disp32 -123 # . . discard args -124 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -125 # check-ints-equal(eax, 0, msg) -126 # . . push args -127 68/push "F - test-allocate-failure: returns null"/imm32 -128 68/push 0/imm32 -129 50/push-eax -130 # . . call -131 e8/call check-ints-equal/disp32 -132 # . . discard args -133 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -134 # no change to ad->curr -135 # . check-ints-equal(ad->curr, 11) -136 # . . push args -137 68/push "F - test-allocate-failure: updates allocation descriptor"/imm32 -138 68/push 0xb/imm32 -139 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx -140 # . . call -141 e8/call check-ints-equal/disp32 -142 # . . discard args -143 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp -144 # . epilog -145 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp -146 5d/pop-to-ebp -147 c3/return -148 -149 # helper: create a nested allocation descriptor (useful for tests) -150 allocate-region: # ad : (address allocation-descriptor), n : int -> new-ad : (address allocation-descriptor) -151 # . prolog -152 55/push-ebp -153 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp -154 # . save registers + 19 == data + 20 + 21 # A default allocation descriptor for programs to use. + 22 Heap: + 23 # curr + 24 0/imm32 + 25 # limit + 26 0/imm32 + 27 + 28 # a reasonable default + 29 Heap-size: + 30 0x200000/imm32/2MB + 31 #? # TODO: reclaim space allocated in tests. + 32 #? 0x2000000/imm32/32MB + 33 + 34 == code + 35 # instruction effective address register displacement immediate + 36 # . op subop mod rm32 base index scale r32 + 37 # . 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 + 38 + 39 # Let's start initializing the default allocation descriptor. + 40 + 41 Entry: + 42 # initialize heap + 43 # . Heap = new-segment(Heap-size) + 44 # . . push args + 45 68/push Heap/imm32 + 46 68/push Heap-size/imm32 + 47 # . . call + 48 e8/call new-segment/disp32 + 49 # . . discard args + 50 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 51 + 52 e8/call run-tests/disp32 # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'. + 53 $array-equal-main:end: + 54 # syscall(exit, Num-test-failures) + 55 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 3/r32/ebx Num-test-failures/disp32 # copy *Num-test-failures to ebx + 56 b8/copy-to-eax 1/imm32/exit + 57 cd/syscall 0x80/imm8 + 58 + 59 # Claim the next 'n' bytes of memory starting at ad->curr and update ad->curr. + 60 # Abort if there isn't enough memory in 'ad'. + 61 allocate: # ad : (address allocation-descriptor), n : int -> address-or-null/eax + 62 # . prolog + 63 55/push-ebp + 64 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp + 65 # . save registers + 66 51/push-ecx + 67 52/push-edx + 68 # ecx = ad + 69 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 8/disp8 . # copy *(ebp+8) to ecx + 70 # save ad->curr + 71 8b/copy 0/mod/indirect 1/rm32/ecx . . . 0/r32/eax . . # copy *ecx to eax + 72 # check if there's enough space + 73 # . edx = ad->curr + n + 74 89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx + 75 03/add 1/mod/*+disp8 5/rm32/ebp . . . 2/r32/edx 0xc/disp8 . # add *(ebp+12) to edx + 76 3b/compare 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # compare edx with *(ecx+4) + 77 73/jump-if-greater-or-equal-signed $allocate:abort/disp8 + 78 $allocate:commit: + 79 # update ad->curr + 80 89/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy edx to *ecx + 81 $allocate:end: + 82 # . restore registers + 83 5a/pop-to-edx + 84 59/pop-to-ecx + 85 # . epilog + 86 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp + 87 5d/pop-to-ebp + 88 c3/return + 89 + 90 $allocate:abort: + 91 # . _write(2/stderr, error) + 92 # . . push args + 93 68/push "allocate: failed to allocate\n"/imm32 + 94 68/push 2/imm32/stderr + 95 # . . call + 96 e8/call _write/disp32 + 97 # . . discard args + 98 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp + 99 # . syscall(exit, 1) +100 bb/copy-to-ebx 1/imm32 +101 b8/copy-to-eax 1/imm32/exit +102 cd/syscall 0x80/imm8 +103 # never gets here +104 +105 test-allocate-success: +106 # . prolog +107 55/push-ebp +108 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp +109 # var ad/ecx : (address allocation-descriptor) = {11, 15} +110 68/push 0xf/imm32/limit +111 68/push 0xb/imm32/curr +112 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx +113 # eax = allocate(ad, 3) +114 # . . push args +115 68/push 3/imm32 +116 51/push-ecx +117 # . . call +118 e8/call allocate/disp32 +119 # . . discard args +120 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +121 # check-ints-equal(eax, 11, msg) +122 # . . push args +123 68/push "F - test-allocate-success: returns current pointer of allocation descriptor"/imm32 +124 68/push 0xb/imm32 +125 50/push-eax +126 # . . call +127 e8/call check-ints-equal/disp32 +128 # . . discard args +129 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +130 # check-ints-equal(ad->curr, 14, msg) +131 # . . push args +132 68/push "F - test-allocate-success: updates allocation descriptor"/imm32 +133 68/push 0xe/imm32 +134 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx +135 # . . call +136 e8/call check-ints-equal/disp32 +137 # . . discard args +138 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +139 # . epilog +140 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp +141 5d/pop-to-ebp +142 c3/return +143 +144 _pending-test-allocate-failure: +145 # . prolog +146 55/push-ebp +147 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp +148 # var ad/ecx : (address allocation-descriptor) = {11, 15} +149 68/push 0xf/imm32/limit +150 68/push 0xb/imm32/curr +151 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx +152 # eax = allocate(ad, 6) +153 # . . push args +154 68/push 6/imm32 155 51/push-ecx -156 # eax = allocate(ad, n) -157 # . . push args -158 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) -159 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) -160 # . . call -161 e8/call allocate/disp32 -162 # . . discard args -163 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -164 # if (eax == 0) abort -165 3d/compare-eax-and 0/imm32 -166 74/jump-if-equal $allocate-region:abort/disp8 -167 # earmark 8 bytes at the start for a new allocation descriptor -168 # . *eax = eax + 8 -169 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx -170 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 8/imm32 # add to ecx -171 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax -172 # . *(eax+4) = eax + n -173 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx -174 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # add *(ebp+12) to ecx -175 89/copy 1/mod/*+disp8 0/rm32/eax . . . 1/r32/ecx 4/disp8 . # copy ecx to *(eax+4) -176 # . restore registers -177 59/pop-to-ecx -178 # . epilog -179 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp -180 5d/pop-to-ebp -181 c3/return -182 -183 # We could create a more general '$abort' jump target, but then we'd need to do -184 # a conditional jump followed by loading the error message and an unconditional -185 # jump. Or we'd need to unconditionally load the error message before a -186 # conditional jump, even if it's unused the vast majority of the time. This way -187 # we bloat a potentially cold segment in RAM so we can abort with a single -188 # instruction. -189 $allocate-region:abort: -190 # . _write(2/stderr, error) -191 # . . push args -192 68/push "allocate-region: failed to allocate\n"/imm32 -193 68/push 2/imm32/stderr -194 # . . call -195 e8/call _write/disp32 -196 # . . discard args -197 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp -198 # . syscall(exit, 1) -199 bb/copy-to-ebx 1/imm32 -200 b8/copy-to-eax 1/imm32/exit -201 cd/syscall 0x80/imm8 -202 # never gets here -203 -204 # . . vim:nowrap:textwidth=0 +156 # . . call +157 e8/call allocate/disp32 +158 # . . discard args +159 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +160 # check-ints-equal(eax, 0, msg) +161 # . . push args +162 68/push "F - test-allocate-failure: returns null"/imm32 +163 68/push 0/imm32 +164 50/push-eax +165 # . . call +166 e8/call check-ints-equal/disp32 +167 # . . discard args +168 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +169 # no change to ad->curr +170 # . check-ints-equal(ad->curr, 11) +171 # . . push args +172 68/push "F - test-allocate-failure: updates allocation descriptor"/imm32 +173 68/push 0xb/imm32 +174 ff 6/subop/push 0/mod/indirect 1/rm32/ecx . . . . . . # push *ecx +175 # . . call +176 e8/call check-ints-equal/disp32 +177 # . . discard args +178 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp +179 # . epilog +180 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp +181 5d/pop-to-ebp +182 c3/return +183 +184 # helper: create a nested allocation descriptor (useful for tests) +185 allocate-region: # ad : (address allocation-descriptor), n : int -> new-ad : (address allocation-descriptor) +186 # . prolog +187 55/push-ebp +188 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp +189 # . save registers +190 51/push-ecx +191 # eax = allocate(ad, n) +192 # . . push args +193 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) +194 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) +195 # . . call +196 e8/call allocate/disp32 +197 # . . discard args +198 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +199 # if (eax == 0) abort +200 3d/compare-eax-and 0/imm32 +201 74/jump-if-equal $allocate-region:abort/disp8 +202 # earmark 8 bytes at the start for a new allocation descriptor +203 # . *eax = eax + 8 +204 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx +205 81 0/subop/add 3/mod/direct 1/rm32/ecx . . . . . 8/imm32 # add to ecx +206 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax +207 # . *(eax+4) = eax + n +208 89/copy 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # copy eax to ecx +209 03/add 1/mod/*+disp8 5/rm32/ebp . . . 1/r32/ecx 0xc/disp8 . # add *(ebp+12) to ecx +210 89/copy 1/mod/*+disp8 0/rm32/eax . . . 1/r32/ecx 4/disp8 . # copy ecx to *(eax+4) +211 # . restore registers +212 59/pop-to-ecx +213 # . epilog +214 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp +215 5d/pop-to-ebp +216 c3/return +217 +218 # We could create a more general '$abort' jump target, but then we'd need to do +219 # a conditional jump followed by loading the error message and an unconditional +220 # jump. Or we'd need to unconditionally load the error message before a +221 # conditional jump, even if it's unused the vast majority of the time. This way +222 # we bloat a potentially cold segment in RAM so we can abort with a single +223 # instruction. +224 $allocate-region:abort: +225 # . _write(2/stderr, error) +226 # . . push args +227 68/push "allocate-region: failed to allocate\n"/imm32 +228 68/push 2/imm32/stderr +229 # . . call +230 e8/call _write/disp32 +231 # . . discard args +232 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp +233 # . syscall(exit, 1) +234 bb/copy-to-ebx 1/imm32 +235 b8/copy-to-eax 1/imm32/exit +236 cd/syscall 0x80/imm8 +237 # never gets here +238 +239 # . . vim:nowrap:textwidth=0 -- cgit 1.4.1-2-gfad0