From 525767cb0e7b6206a4785673d6e1babd567b5e71 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Sun, 10 Nov 2019 21:04:21 -0800 Subject: 5738 --- html/apps/mu.subx.html | 1979 ++++++++++++++++++++++++++---------------------- 1 file changed, 1081 insertions(+), 898 deletions(-) (limited to 'html') diff --git a/html/apps/mu.subx.html b/html/apps/mu.subx.html index 997c37bd..b6c3116c 100644 --- a/html/apps/mu.subx.html +++ b/html/apps/mu.subx.html @@ -142,912 +142,1095 @@ if ('onhashchange' in window) { 81 # A program is a linked list of functions 82 # A function contains: 83 # name: string - 84 # inouts: linked list of var-types <-- 'inouts' is more precise than 'inputs' - 85 # outputs: linked list of var-types - 86 # body: block - 87 # A var-type contains: - 88 # name: string - 89 # type: s-expression of type ids - 90 # Statements are not yet fully designed. - 91 # statement = var definition or simple statement or block - 92 # block = linked list of statements - 93 - 94 # == Translation - 95 # Now that we know what the language looks like in the large, let's think - 96 # about how translation happens from the bottom up. The interplay between - 97 # variable scopes and statements using variables is the most complex aspect of - 98 # translation. - 99 # - 100 # Assume that we maintain a 'functions' list while parsing source code. And a - 101 # 'primitives' list is a global constant. Both these contain enough information - 102 # to perform type-checking on function calls or primitive statements, respectively. - 103 # - 104 # Defining variables pushes them on a stack with the current block depth and - 105 # enough information about their location (stack offset or register id). - 106 # Starting a block increments the current block id. - 107 # Each statement now has enough information to emit code for it. - 108 # Ending a block is where the magic happens: - 109 # pop all variables at the current block depth - 110 # emit code to restore all register variables introduced at the current depth - 111 # emit code to clean up all stack variables at the current depth (just increment esp) - 112 # decrement the current block depth - 113 # - 114 # One additional check we'll need is to ensure that a variable in a register - 115 # isn't shadowed by a different one. That may be worth a separate data - 116 # structure but for now repeatedly scanning the var stack should suffice. - 117 # - 118 # Formal types: - 119 # functions, primitives: linked list of info - 120 # name: string - 121 # inouts: linked list of var-types - 122 # outputs: linked list of var-types - 123 # vars: linked list (stack) of info - 124 # name: string - 125 # type: s-expression? Just a type id for now. - 126 # block: int - 127 # location: int (negative numbers are on the stack; - 128 # 0-7 are in registers; - 129 # higher positive numbers are currently invalid) - 130 - 131 # == Compiling a single instruction - 132 # Determine the function or primitive being called. - 133 # If no matches, show all functions/primitives with the same name, along - 134 # with reasons they don't match. (type and storage checking) - 135 # It must be a function if: - 136 # #outputs > 1, or - 137 # #inouts > 2, or - 138 # #inouts + #outputs > 2 - 139 # If it's a function, emit: - 140 # (low-level-name <rm32 or imm32>...) - 141 # Otherwise (it's a primitive): - 142 # assert(#inouts <= 2 && #outs <= 1 && (#inouts + #outs) <= 2) - 143 # emit opcode - 144 # emit-rm32(inout[0]) - 145 # if out[0] exists: emit-r32(out[0]) - 146 # else if inout[1] is a literal: emit-imm32(inout[1]) - 147 # else: emit-rm32(inout[1]) - 148 - 149 # emit-rm32 and emit-r32 should check that the variable they intend is still - 150 # available in the register. - 151 - 152 # == Emitting a block - 153 # Emit block name if necessary - 154 # Emit '{' - 155 # When you encounter a statement, emit it as above - 156 # When you encounter a variable declaration - 157 # emit any code needed for it (bzeros) - 158 # push it on the var stack - 159 # update register dict if necessary - 160 # When you encounter '}' - 161 # While popping variables off the var stack until block id changes - 162 # Emit code needed to clean up the stack - 163 # either increment esp - 164 # or pop into appropriate register - 165 # TODO: how to update the register dict? does it need to be a stack as well? - 166 - 167 # The rest is straightforward. - 168 - 169 == data - 170 - 171 Program: # (address function) - 172 0/imm32 - 173 - 174 == code - 175 - 176 Entry: - 177 # . prologue - 178 89/<- %ebp 4/r32/esp - 179 (new-segment Heap-size Heap) - 180 # if (argv[1] == "test') run-tests() - 181 { - 182 # if (argc <= 1) break - 183 81 7/subop/compare *ebp 1/imm32 - 184 7e/jump-if-lesser-or-equal break/disp8 - 185 # if (argv[1] != "test") break - 186 (kernel-string-equal? *(ebp+8) "test") # => eax - 187 3d/compare-eax-and 0/imm32 - 188 74/jump-if-equal break/disp8 - 189 # - 190 (run-tests) - 191 # syscall(exit, *Num-test-failures) - 192 8b/-> *Num-test-failures 3/r32/ebx - 193 eb/jump $mu-main:end/disp8 - 194 } - 195 # otherwise convert Stdin - 196 (convert-mu Stdin Stdout) - 197 (flush Stdout) - 198 # syscall(exit, 0) - 199 bb/copy-to-ebx 0/imm32 - 200 $mu-main:end: - 201 b8/copy-to-eax 1/imm32/exit - 202 cd/syscall 0x80/imm8 - 203 - 204 convert-mu: # in : (address buffered-file), out : (address buffered-file) - 205 # . prologue - 206 55/push-ebp - 207 89/<- %ebp 4/r32/esp - 208 # - 209 (parse-mu *(ebp+8)) - 210 (check-mu-types) - 211 (emit-subx *(ebp+0xc)) - 212 $convert-mu:end: - 213 # . epilogue - 214 89/<- %esp 5/r32/ebp - 215 5d/pop-to-ebp - 216 c3/return - 217 - 218 test-convert-empty-input: - 219 # empty input => empty output - 220 # . prologue - 221 55/push-ebp - 222 89/<- %ebp 4/r32/esp - 223 # setup - 224 (clear-stream _test-input-stream) - 225 (clear-stream _test-input-buffered-file->buffer) - 226 (clear-stream _test-output-stream) - 227 (clear-stream _test-output-buffered-file->buffer) - 228 # - 229 (convert-mu _test-input-buffered-file _test-output-buffered-file) - 230 (flush _test-output-buffered-file) - 231 (check-stream-equal _test-output-stream "" "F - test-convert-empty-input") - 232 # . epilogue - 233 89/<- %esp 5/r32/ebp - 234 5d/pop-to-ebp - 235 c3/return - 236 - 237 test-convert-function-skeleton: - 238 # empty function decl => function prologue and epilogue - 239 # fn foo { - 240 # } - 241 # => - 242 # foo: - 243 # # . prologue - 244 # 55/push-ebp - 245 # 89/<- %ebp 4/r32/esp - 246 # # . epilogue - 247 # 89/<- %esp 5/r32/ebp - 248 # 5d/pop-to-ebp - 249 # c3/return + 84 # inouts: linked list of vars <-- 'inouts' is more precise than 'inputs' + 85 # data: (address var) + 86 # next: (address list) + 87 # outputs: linked list of vars + 88 # data: (address var) + 89 # next: (address list) + 90 # body: block + 91 # A var-type contains: + 92 # name: string + 93 # type: s-expression of type ids + 94 # Statements are not yet fully designed. + 95 # statement = var definition or simple statement or block + 96 # simple statement: + 97 # name: string + 98 # inouts: linked list of vars + 99 # outputs: linked list of vars + 100 # block = linked list of statements + 101 + 102 # == Translation + 103 # Now that we know what the language looks like in the large, let's think + 104 # about how translation happens from the bottom up. The interplay between + 105 # variable scopes and statements using variables is the most complex aspect of + 106 # translation. + 107 # + 108 # Assume that we maintain a 'functions' list while parsing source code. And a + 109 # 'primitives' list is a global constant. Both these contain enough information + 110 # to perform type-checking on function calls or primitive statements, respectively. + 111 # + 112 # Defining variables pushes them on a stack with the current block depth and + 113 # enough information about their location (stack offset or register id). + 114 # Starting a block increments the current block id. + 115 # Each statement now has enough information to emit code for it. + 116 # Ending a block is where the magic happens: + 117 # pop all variables at the current block depth + 118 # emit code to restore all register variables introduced at the current depth + 119 # emit code to clean up all stack variables at the current depth (just increment esp) + 120 # decrement the current block depth + 121 # + 122 # One additional check we'll need is to ensure that a variable in a register + 123 # isn't shadowed by a different one. That may be worth a separate data + 124 # structure but for now repeatedly scanning the var stack should suffice. + 125 # + 126 # Formal types: + 127 # functions, primitives: linked list of info + 128 # name: string + 129 # inouts: linked list of vars + 130 # outputs: linked list of vars + 131 # live-vars: stack of vars + 132 # var: + 133 # name: string + 134 # type: s-expression? Just a type id for now. + 135 # block: int + 136 # location: int (negative numbers are on the stack; + 137 # 0-7 are in registers; + 138 # higher positive numbers are currently invalid) + 139 + 140 # == Compiling a single instruction + 141 # Determine the function or primitive being called. + 142 # If no matches, show all functions/primitives with the same name, along + 143 # with reasons they don't match. (type and storage checking) + 144 # It must be a function if: + 145 # #outputs > 1, or + 146 # #inouts > 2, or + 147 # #inouts + #outputs > 2 + 148 # If it's a function, emit: + 149 # (low-level-name <rm32 or imm32>...) + 150 # Otherwise (it's a primitive): + 151 # assert(#inouts <= 2 && #outs <= 1 && (#inouts + #outs) <= 2) + 152 # emit opcode + 153 # emit-rm32(inout[0]) + 154 # if out[0] exists: emit-r32(out[0]) + 155 # else if inout[1] is a literal: emit-imm32(inout[1]) + 156 # else: emit-rm32(inout[1]) + 157 + 158 # emit-rm32 and emit-r32 should check that the variable they intend is still + 159 # available in the register. + 160 + 161 # == Emitting a block + 162 # Emit block name if necessary + 163 # Emit '{' + 164 # When you encounter a statement, emit it as above + 165 # When you encounter a variable declaration + 166 # emit any code needed for it (bzeros) + 167 # push it on the var stack + 168 # update register dict if necessary + 169 # When you encounter '}' + 170 # While popping variables off the var stack until block id changes + 171 # Emit code needed to clean up the stack + 172 # either increment esp + 173 # or pop into appropriate register + 174 + 175 # The rest is straightforward. + 176 + 177 == data + 178 + 179 Program: # (address function) + 180 0/imm32 + 181 + 182 Function-name: + 183 0/imm32 + 184 Function-subx-name: + 185 4/imm32 + 186 Function-inouts: # (address list var) + 187 8/imm32 + 188 Function-outputs: # (address list var) + 189 0xc/imm32 + 190 Function-body: # (address block) + 191 0x10/imm32 + 192 Function-next: # (address function) + 193 0x14/imm32 + 194 Function-size: + 195 0x18/imm32/24 + 196 + 197 Stmt-name: + 198 0/imm32 + 199 Stmt-inouts: + 200 4/imm32 + 201 Stmt-outputs: + 202 8/imm32 + 203 Stmt-next: + 204 0xc/imm32 + 205 Stmt-size: + 206 0x10/imm32 + 207 + 208 Var-name: + 209 0/imm32 + 210 Var-type: + 211 4/imm32 + 212 Var-location: + 213 8/imm32 + 214 Var-block: + 215 0xc/imm32 + 216 Var-size: + 217 0x10/imm32 + 218 + 219 == code + 220 + 221 Entry: + 222 # . prologue + 223 89/<- %ebp 4/r32/esp + 224 (new-segment Heap-size Heap) + 225 # if (argv[1] == "test') run-tests() + 226 { + 227 # if (argc <= 1) break + 228 81 7/subop/compare *ebp 1/imm32 + 229 7e/jump-if-lesser-or-equal break/disp8 + 230 # if (argv[1] != "test") break + 231 (kernel-string-equal? *(ebp+8) "test") # => eax + 232 3d/compare-eax-and 0/imm32 + 233 74/jump-if-equal break/disp8 + 234 # + 235 (run-tests) + 236 # syscall(exit, *Num-test-failures) + 237 8b/-> *Num-test-failures 3/r32/ebx + 238 eb/jump $mu-main:end/disp8 + 239 } + 240 # otherwise convert Stdin + 241 (convert-mu Stdin Stdout) + 242 (flush Stdout) + 243 # syscall(exit, 0) + 244 bb/copy-to-ebx 0/imm32 + 245 $mu-main:end: + 246 b8/copy-to-eax 1/imm32/exit + 247 cd/syscall 0x80/imm8 + 248 + 249 convert-mu: # in : (address buffered-file), out : (address buffered-file) 250 # . prologue 251 55/push-ebp 252 89/<- %ebp 4/r32/esp - 253 # setup - 254 (clear-stream _test-input-stream) - 255 (clear-stream _test-input-buffered-file->buffer) - 256 (clear-stream _test-output-stream) - 257 (clear-stream _test-output-buffered-file->buffer) - 258 # - 259 (write _test-input-stream "fn foo {\n") - 260 (write _test-input-stream "}\n") - 261 # convert - 262 (convert-mu _test-input-buffered-file _test-output-buffered-file) - 263 (flush _test-output-buffered-file) - 264 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- - 270 # check output - 271 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0") - 272 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-function-skeleton/1") - 273 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-function-skeleton/2") - 274 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3") - 275 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-function-skeleton/4") - 276 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5") - 277 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-function-skeleton/6") - 278 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-function-skeleton/7") - 279 # . epilogue - 280 89/<- %esp 5/r32/ebp - 281 5d/pop-to-ebp - 282 c3/return - 283 - 284 test-convert-multiple-function-skeletons: - 285 # multiple functions correctly organized into a linked list - 286 # fn foo { - 287 # } - 288 # fn bar { - 289 # } - 290 # => - 291 # foo: - 292 # # . prologue - 293 # 55/push-ebp - 294 # 89/<- %ebp 4/r32/esp - 295 # # . epilogue - 296 # 89/<- %esp 5/r32/ebp - 297 # 5d/pop-to-ebp - 298 # c3/return - 299 # bar: - 300 # # . prologue - 301 # 55/push-ebp - 302 # 89/<- %ebp 4/r32/esp - 303 # # . epilogue - 304 # 89/<- %esp 5/r32/ebp - 305 # 5d/pop-to-ebp - 306 # c3/return - 307 # . prologue - 308 55/push-ebp - 309 89/<- %ebp 4/r32/esp - 310 # setup - 311 (clear-stream _test-input-stream) - 312 (clear-stream _test-input-buffered-file->buffer) - 313 (clear-stream _test-output-stream) - 314 (clear-stream _test-output-buffered-file->buffer) - 315 # - 316 (write _test-input-stream "fn foo {\n") - 317 (write _test-input-stream "}\n") - 318 (write _test-input-stream "fn bar {\n") - 319 (write _test-input-stream "}\n") - 320 # convert - 321 (convert-mu _test-input-buffered-file _test-output-buffered-file) - 322 (flush _test-output-buffered-file) - 323 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- - 329 # check first function - 330 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-multiple-function-skeletons/0") - 331 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-multiple-function-skeletons/1") - 332 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-multiple-function-skeletons/2") - 333 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/3") - 334 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-multiple-function-skeletons/4") - 335 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/5") - 336 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/6") - 337 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-multiple-function-skeletons/7") - 338 # check second function - 339 (check-next-stream-line-equal _test-output-stream "bar:" "F - test-convert-multiple-function-skeletons/10") - 340 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-multiple-function-skeletons/11") - 341 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-multiple-function-skeletons/12") - 342 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/13") - 343 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-multiple-function-skeletons/14") - 344 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/15") - 345 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/16") - 346 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-multiple-function-skeletons/17") - 347 # . epilogue - 348 89/<- %esp 5/r32/ebp - 349 5d/pop-to-ebp - 350 c3/return - 351 - 352 test-convert-function-with-arg: - 353 # function with one arg and a copy instruction - 354 # fn foo n : int -> result/eax : int { - 355 # result <- copy n - 356 # } - 357 # => - 358 # foo: - 359 # # . prologue - 360 # 55/push-ebp - 361 # 89/<- %ebp 4/r32/esp - 362 # { - 363 # # result <- copy n - 364 # 8b/-> *(ebp+8) 0/r32/eax - 365 # } - 366 # # . epilogue - 367 # 89/<- %esp 5/r32/ebp - 368 # 5d/pop-to-ebp - 369 # c3/return - 370 # . prologue - 371 55/push-ebp - 372 89/<- %ebp 4/r32/esp - 373 # setup - 374 (clear-stream _test-input-stream) - 375 (clear-stream _test-input-buffered-file->buffer) - 376 (clear-stream _test-output-stream) - 377 (clear-stream _test-output-buffered-file->buffer) - 378 # - 379 (write _test-input-stream "fn foo {\n") - 380 (write _test-input-stream "}\n") - 381 # convert - 382 (convert-mu _test-input-buffered-file _test-output-buffered-file) - 383 (flush _test-output-buffered-file) - 384 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- - 390 # check output - 391 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0") - 392 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-function-skeleton/1") - 393 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-function-skeleton/2") - 394 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3") - 395 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-function-skeleton/4") - 396 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5") - 397 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-function-skeleton/6") - 398 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-function-skeleton/7") - 399 # . epilogue - 400 89/<- %esp 5/r32/ebp - 401 5d/pop-to-ebp - 402 c3/return - 403 - 404 parse-mu: # in : (address buffered-file) - 405 # pseudocode - 406 # var curr-function = Program - 407 # var line : (stream byte 512) - 408 # var word-slice : slice - 409 # while true # line loop - 410 # clear-stream(line) - 411 # read-line-buffered(in, line) - 412 # if (line->write == 0) break # end of file - 413 # while true # word loop - 414 # word-slice = next-word-or-string(line) - 415 # if slice-empty?(word-slice) # end of line - 416 # break - 417 # else if slice-starts-with?(word-slice, "#") # comment - 418 # break # end of line - 419 # else if slice-equal(word-slice, "fn") - 420 # var new-function : (address function) = new function - 421 # populate-mu-function(in, new-function) - 422 # *curr-function = new-function - 423 # curr-function = &new-function->next - 424 # else - 425 # abort() - 426 # - 427 # . prologue - 428 55/push-ebp - 429 89/<- %ebp 4/r32/esp - 430 # . save registers - 431 50/push-eax - 432 51/push-ecx - 433 52/push-edx - 434 57/push-edi - 435 # var line/ecx : (stream byte 512) - 436 81 5/subop/subtract %esp 0x200/imm32 - 437 68/push 0x200/imm32/length - 438 68/push 0/imm32/read - 439 68/push 0/imm32/write - 440 89/<- %ecx 4/r32/esp - 441 # var word-slice/edx : slice - 442 68/push 0/imm32/end - 443 68/push 0/imm32/start - 444 89/<- %edx 4/r32/esp - 445 # var curr-function/edi : (address function) = Program - 446 bf/copy-to-edi Program/imm32 - 447 { - 448 $parse-mu:line-loop: - 449 (clear-stream %ecx) - 450 (read-line-buffered *(ebp+8) %ecx) - 451 # if (line->write == 0) break - 452 81 7/subop/compare *ecx 0/imm32 - 453 0f 84/jump-if-equal break/disp32 - 454 +-- 6 lines: #? # dump line --------------------------------------------------------------------------------------------------------------------------- - 460 { # word loop - 461 $parse-mu:word-loop: - 462 (next-word-or-string %ecx %edx) - 463 # if slice-empty?(word-slice) break - 464 (slice-empty? %edx) - 465 3d/compare-eax-and 0/imm32 - 466 0f 85/jump-if-not-equal break/disp32 - 467 # if (*word-slice->start == "#") break - 468 # . eax = *word-slice->start - 469 8b/-> *edx 0/r32/eax - 470 8a/copy-byte *eax 0/r32/AL - 471 81 4/subop/and %eax 0xff/imm32 - 472 # . if (eax == '#') break - 473 3d/compare-eax-and 0x23/imm32/hash - 474 0f 84/jump-if-equal break/disp32 - 475 # if (slice-equal?(word-slice, "fn")) parse a function - 476 { - 477 (slice-equal? %edx "fn") - 478 3d/compare-eax-and 0/imm32 - 479 0f 84/jump-if-equal break/disp32 - 480 # var new-function/eax : (address function) = populate-mu-function() - 481 (allocate Heap *Function-size) # => eax - 482 (populate-mu-function-header %ecx %eax) - 483 (populate-mu-function-body *(ebp+8) %eax) - 484 # *curr-function = new-function - 485 89/<- *edi 0/r32/eax - 486 # curr-function = &new-function->next - 487 8d/address-> *(eax+0x10) 7/r32/edi - 488 e9/jump $parse-mu:word-loop/disp32 - 489 } - 490 # otherwise abort - 491 e9/jump $parse-mu:abort/disp32 - 492 } # end word loop - 493 e9/jump loop/disp32 - 494 } # end line loop - 495 $parse-mu:end: - 496 # . reclaim locals - 497 81 0/subop/add %esp 0x214/imm32 - 498 # . restore registers - 499 5f/pop-to-edi - 500 5a/pop-to-edx - 501 59/pop-to-ecx - 502 58/pop-to-eax - 503 # . epilogue - 504 89/<- %esp 5/r32/ebp - 505 5d/pop-to-ebp - 506 c3/return - 507 - 508 $parse-mu:abort: - 509 # error("unexpected top-level command: " word-slice "\n") - 510 (write-buffered Stderr "unexpected top-level command: ") - 511 (write-buffered Stderr %edx) - 512 (write-buffered Stderr "\n") - 513 (flush Stderr) - 514 # . syscall(exit, 1) - 515 bb/copy-to-ebx 1/imm32 - 516 b8/copy-to-eax 1/imm32/exit - 517 cd/syscall 0x80/imm8 - 518 # never gets here - 519 - 520 # errors considered: - 521 # fn foo { { - 522 # fn foo { } - 523 # fn foo { } { - 524 # fn foo # no block - 525 populate-mu-function-header: # first-line : (address stream byte), out : (address function) - 526 # . prologue - 527 55/push-ebp - 528 89/<- %ebp 4/r32/esp - 529 # . save registers - 530 50/push-eax - 531 51/push-ecx - 532 57/push-edi - 533 # edi = out - 534 8b/-> *(ebp+0xc) 7/r32/edi - 535 # var word-slice/ecx : slice - 536 68/push 0/imm32/end - 537 68/push 0/imm32/start - 538 89/<- %ecx 4/r32/esp - 539 # save function name - 540 (next-word *(ebp+8) %ecx) - 541 (slice-to-string Heap %ecx) # => eax - 542 89/<- *edi 0/r32/eax - 543 # assert that next token is '{' - 544 (next-word *(ebp+8) %ecx) - 545 (slice-equal? %ecx "{") - 546 3d/compare-eax-and 0/imm32 - 547 74/jump-if-equal $populate-mu-function-header:abort/disp8 - 548 # assert that there's no further token - 549 { - 550 # word-slice = next-word(line) - 551 (next-word *(ebp+8) %ecx) - 552 # if (word-slice == '') break - 553 (slice-empty? %ecx) - 554 3d/compare-eax-and 0/imm32 - 555 75/jump-if-not-equal break/disp8 - 556 # if (slice-starts-with?(word-slice, "#")) break - 557 # . eax = *word-slice->start - 558 8b/-> *edx 0/r32/eax - 559 8a/copy-byte *eax 0/r32/AL - 560 81 4/subop/and %eax 0xff/imm32 - 561 # . if (eax == '#') break - 562 3d/compare-eax-and 0x23/imm32/hash - 563 74/jump-if-equal break/disp8 - 564 # otherwise abort - 565 eb/jump $populate-mu-function-header:abort/disp8 - 566 } - 567 $populate-mu-function-header:end: - 568 # . reclaim locals - 569 81 0/subop/add %esp 8/imm32 - 570 # . restore registers - 571 5f/pop-to-edi - 572 59/pop-to-ecx - 573 58/pop-to-eax - 574 # . epilogue - 575 89/<- %esp 5/r32/ebp - 576 5d/pop-to-ebp - 577 c3/return - 578 - 579 $populate-mu-function-header:abort: - 580 # error("function header not in form 'fn <name> {'") - 581 (write-buffered Stderr "function header not in form 'fn <name> {' -- '") - 582 (rewind-stream *(ebp+8)) - 583 (write-stream 2 *(ebp+8)) - 584 (write-buffered Stderr "'\n") - 585 (flush Stderr) - 586 # . syscall(exit, 1) - 587 bb/copy-to-ebx 1/imm32 - 588 b8/copy-to-eax 1/imm32/exit - 589 cd/syscall 0x80/imm8 - 590 # never gets here - 591 - 592 # errors considered: - 593 # { abc - 594 populate-mu-function-body: # in : (address buffered-file), out : (address function) - 595 # . prologue - 596 55/push-ebp - 597 89/<- %ebp 4/r32/esp - 598 # . save registers - 599 50/push-eax - 600 51/push-ecx - 601 52/push-edx - 602 53/push-ebx - 603 # var line/ecx : (stream byte 512) - 604 81 5/subop/subtract %esp 0x200/imm32 - 605 68/push 0x200/imm32/length - 606 68/push 0/imm32/read - 607 68/push 0/imm32/write - 608 89/<- %ecx 4/r32/esp - 609 # var word-slice/edx : slice - 610 68/push 0/imm32/end - 611 68/push 0/imm32/start - 612 89/<- %edx 4/r32/esp - 613 # var open-curly-count/ebx : int = 1 - 614 bb/copy-to-ebx 1/imm32 - 615 { # line loop - 616 $populate-mu-function-body:line-loop: - 617 # if (open-curly-count == 0) break - 618 81 7/subop/compare %ebx 0/imm32 - 619 0f 84/jump-if-equal break/disp32 - 620 # line = read-line-buffered(in) - 621 (clear-stream %ecx) - 622 (read-line-buffered *(ebp+8) %ecx) - 623 # if (line->write == 0) break - 624 81 7/subop/compare *ecx 0/imm32 - 625 0f 84/jump-if-equal break/disp32 - 626 # word-slice = next-word(line) - 627 (next-word %ecx %edx) - 628 # if slice-empty?(word-slice) continue - 629 (slice-empty? %ecx) - 630 3d/compare-eax-and 0/imm32 - 631 75/jump-if-not-equal loop/disp8 - 632 # if (slice-starts-with?(word-slice, '#') continue - 633 # . eax = *word-slice->start - 634 8b/-> *edx 0/r32/eax - 635 8a/copy-byte *eax 0/r32/AL - 636 81 4/subop/and %eax 0xff/imm32 - 637 # . if (eax == '#') continue - 638 3d/compare-eax-and 0x23/imm32/hash - 639 74/jump-if-equal loop/disp8 - 640 { - 641 # if slice-equal?(word-slice, "{") ++open-curly-count - 642 { - 643 (slice-equal? %ecx "{") - 644 3d/compare-eax-and 0/imm32 - 645 74/jump-if-equal break/disp8 - 646 43/increment-ebx - 647 eb/jump $curly-found:end/disp8 - 648 } - 649 # else if slice-equal?(word-slice, "}") --open-curly-count - 650 { - 651 (slice-equal? %ecx "}") - 652 3d/compare-eax-and 0/imm32 - 653 74/jump-if-equal break/disp8 - 654 4b/decrement-ebx - 655 eb/jump $curly-found:end/disp8 - 656 } - 657 # else break - 658 eb/jump $populate-mu-function-body:end/disp8 - 659 } - 660 # - check for invalid tokens after curly - 661 $curly-found:end: - 662 # second-word-slice = next-word(line) - 663 (next-word %ecx %edx) - 664 # if slice-empty?(second-word-slice) continue - 665 (slice-empty? %ecx) - 666 3d/compare-eax-and 0/imm32 - 667 0f 85/jump-if-not-equal loop/disp32 - 668 # if (slice-starts-with?(second-word-slice, '#') continue - 669 # . eax = *second-word-slice->start - 670 8b/-> *edx 0/r32/eax - 671 8a/copy-byte *eax 0/r32/AL - 672 81 4/subop/and %eax 0xff/imm32 - 673 # . if (eax == '#') continue - 674 3d/compare-eax-and 0x23/imm32/hash - 675 0f 84/jump-if-equal loop/disp32 - 676 # abort - 677 eb/jump $populate-mu-function-body:abort/disp8 - 678 } # end line loop - 679 $populate-mu-function-body:end: - 680 # . reclaim locals - 681 81 0/subop/add %esp 0x214/imm32 - 682 # . restore registers - 683 5b/pop-to-ebx - 684 5a/pop-to-edx - 685 59/pop-to-ecx - 686 58/pop-to-eax - 687 # . epilogue - 688 89/<- %esp 5/r32/ebp - 689 5d/pop-to-ebp - 690 c3/return - 691 - 692 $populate-mu-function-body:abort: - 693 # error("'{' or '}' should be on its own line, but got '") - 694 (write-buffered Stderr "'{' or '}' should be on its own line, but got '") - 695 (rewind-stream %ecx) - 696 (write-stream 2 %ecx) - 697 (write-buffered Stderr "'\n") - 698 (flush Stderr) - 699 # . syscall(exit, 1) - 700 bb/copy-to-ebx 1/imm32 - 701 b8/copy-to-eax 1/imm32/exit - 702 cd/syscall 0x80/imm8 - 703 # never gets here - 704 - 705 check-mu-types: - 706 # . prologue - 707 55/push-ebp - 708 89/<- %ebp 4/r32/esp - 709 # - 710 $check-types:end: - 711 # . epilogue - 712 89/<- %esp 5/r32/ebp - 713 5d/pop-to-ebp - 714 c3/return - 715 - 716 emit-subx: # out : (address buffered-file) - 717 # . prologue - 718 55/push-ebp - 719 89/<- %ebp 4/r32/esp - 720 # . save registers - 721 50/push-eax - 722 51/push-ecx - 723 57/push-edi - 724 # edi = out - 725 8b/-> *(ebp+8) 7/r32/edi - 726 # var curr/ecx : (address function) = Program - 727 8b/-> *Program 1/r32/ecx - 728 { - 729 # if (curr == NULL) break - 730 81 7/subop/compare %ecx 0/imm32 - 731 0f 84/jump-if-equal break/disp32 - 732 (emit-subx-function %edi %ecx) - 733 # curr = curr->next - 734 8b/-> *(ecx+0x10) 1/r32/ecx - 735 e9/jump loop/disp32 - 736 } - 737 $emit-subx:end: - 738 # . restore registers - 739 5f/pop-to-edi - 740 59/pop-to-ecx - 741 58/pop-to-eax - 742 # . epilogue - 743 89/<- %esp 5/r32/ebp - 744 5d/pop-to-ebp - 745 c3/return - 746 - 747 # == Emitting a function - 748 # Emit function header - 749 # Emit function prologue - 750 # Translate function body - 751 # Emit function epilogue - 752 - 753 emit-subx-function: # out : (address buffered-file), f : (address function) - 754 # . prologue - 755 55/push-ebp - 756 89/<- %ebp 4/r32/esp - 757 # . save registers - 758 50/push-eax - 759 51/push-ecx - 760 57/push-edi - 761 # edi = out - 762 8b/-> *(ebp+8) 7/r32/edi - 763 # ecx = f - 764 8b/-> *(ebp+0xc) 1/r32/ecx - 765 # - 766 (write-buffered %edi *ecx) - 767 (write-buffered %edi ":\n") - 768 (emit-subx-prologue %edi) - 769 (emit-subx-block %edi *(ecx+4)) # TODO: offset - 770 (emit-subx-epilogue %edi) - 771 $emit-subx-function:end: - 772 # . restore registers - 773 5f/pop-to-edi - 774 59/pop-to-ecx - 775 58/pop-to-eax - 776 # . epilogue - 777 89/<- %esp 5/r32/ebp - 778 5d/pop-to-ebp - 779 c3/return - 780 - 781 emit-subx-block: # out : (address buffered-file), block : (address block) - 782 # . prologue - 783 55/push-ebp - 784 89/<- %ebp 4/r32/esp - 785 # - 786 $emit-subx-block:end: + 253 # + 254 (parse-mu *(ebp+8)) + 255 (check-mu-types) + 256 (emit-subx *(ebp+0xc)) + 257 $convert-mu:end: + 258 # . epilogue + 259 89/<- %esp 5/r32/ebp + 260 5d/pop-to-ebp + 261 c3/return + 262 + 263 test-convert-empty-input: + 264 # empty input => empty output + 265 # . prologue + 266 55/push-ebp + 267 89/<- %ebp 4/r32/esp + 268 # setup + 269 (clear-stream _test-input-stream) + 270 (clear-stream _test-input-buffered-file->buffer) + 271 (clear-stream _test-output-stream) + 272 (clear-stream _test-output-buffered-file->buffer) + 273 # + 274 (convert-mu _test-input-buffered-file _test-output-buffered-file) + 275 (flush _test-output-buffered-file) + 276 (check-stream-equal _test-output-stream "" "F - test-convert-empty-input") + 277 # . epilogue + 278 89/<- %esp 5/r32/ebp + 279 5d/pop-to-ebp + 280 c3/return + 281 + 282 test-convert-function-skeleton: + 283 # empty function decl => function prologue and epilogue + 284 # fn foo { + 285 # } + 286 # => + 287 # foo: + 288 # # . prologue + 289 # 55/push-ebp + 290 # 89/<- %ebp 4/r32/esp + 291 # # . epilogue + 292 # 89/<- %esp 5/r32/ebp + 293 # 5d/pop-to-ebp + 294 # c3/return + 295 # . prologue + 296 55/push-ebp + 297 89/<- %ebp 4/r32/esp + 298 # setup + 299 (clear-stream _test-input-stream) + 300 (clear-stream _test-input-buffered-file->buffer) + 301 (clear-stream _test-output-stream) + 302 (clear-stream _test-output-buffered-file->buffer) + 303 # + 304 (write _test-input-stream "fn foo {\n") + 305 (write _test-input-stream "}\n") + 306 # convert + 307 (convert-mu _test-input-buffered-file _test-output-buffered-file) + 308 (flush _test-output-buffered-file) + 309 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- + 315 # check output + 316 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0") + 317 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-function-skeleton/1") + 318 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-function-skeleton/2") + 319 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3") + 320 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-function-skeleton/4") + 321 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5") + 322 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-function-skeleton/6") + 323 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-function-skeleton/7") + 324 # . epilogue + 325 89/<- %esp 5/r32/ebp + 326 5d/pop-to-ebp + 327 c3/return + 328 + 329 test-convert-multiple-function-skeletons: + 330 # multiple functions correctly organized into a linked list + 331 # fn foo { + 332 # } + 333 # fn bar { + 334 # } + 335 # => + 336 # foo: + 337 # # . prologue + 338 # 55/push-ebp + 339 # 89/<- %ebp 4/r32/esp + 340 # # . epilogue + 341 # 89/<- %esp 5/r32/ebp + 342 # 5d/pop-to-ebp + 343 # c3/return + 344 # bar: + 345 # # . prologue + 346 # 55/push-ebp + 347 # 89/<- %ebp 4/r32/esp + 348 # # . epilogue + 349 # 89/<- %esp 5/r32/ebp + 350 # 5d/pop-to-ebp + 351 # c3/return + 352 # . prologue + 353 55/push-ebp + 354 89/<- %ebp 4/r32/esp + 355 # setup + 356 (clear-stream _test-input-stream) + 357 (clear-stream _test-input-buffered-file->buffer) + 358 (clear-stream _test-output-stream) + 359 (clear-stream _test-output-buffered-file->buffer) + 360 # + 361 (write _test-input-stream "fn foo {\n") + 362 (write _test-input-stream "}\n") + 363 (write _test-input-stream "fn bar {\n") + 364 (write _test-input-stream "}\n") + 365 # convert + 366 (convert-mu _test-input-buffered-file _test-output-buffered-file) + 367 (flush _test-output-buffered-file) + 368 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- + 374 # check first function + 375 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-multiple-function-skeletons/0") + 376 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-multiple-function-skeletons/1") + 377 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-multiple-function-skeletons/2") + 378 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/3") + 379 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-multiple-function-skeletons/4") + 380 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/5") + 381 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/6") + 382 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-multiple-function-skeletons/7") + 383 # check second function + 384 (check-next-stream-line-equal _test-output-stream "bar:" "F - test-convert-multiple-function-skeletons/10") + 385 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-multiple-function-skeletons/11") + 386 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-multiple-function-skeletons/12") + 387 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/13") + 388 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-multiple-function-skeletons/14") + 389 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/15") + 390 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/16") + 391 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-multiple-function-skeletons/17") + 392 # . epilogue + 393 89/<- %esp 5/r32/ebp + 394 5d/pop-to-ebp + 395 c3/return + 396 + 397 test-convert-function-with-arg: + 398 # function with one arg and a copy instruction + 399 # fn foo n : int -> result/eax : int { + 400 # result <- copy n + 401 # } + 402 # => + 403 # foo: + 404 # # . prologue + 405 # 55/push-ebp + 406 # 89/<- %ebp 4/r32/esp + 407 # { + 408 # # result <- copy n + 409 # 8b/-> *(ebp+8) 0/r32/eax + 410 # } + 411 # # . epilogue + 412 # 89/<- %esp 5/r32/ebp + 413 # 5d/pop-to-ebp + 414 # c3/return + 415 # . prologue + 416 55/push-ebp + 417 89/<- %ebp 4/r32/esp + 418 # setup + 419 (clear-stream _test-input-stream) + 420 (clear-stream _test-input-buffered-file->buffer) + 421 (clear-stream _test-output-stream) + 422 (clear-stream _test-output-buffered-file->buffer) + 423 # + 424 (write _test-input-stream "fn foo {\n") + 425 (write _test-input-stream "}\n") + 426 # convert + 427 (convert-mu _test-input-buffered-file _test-output-buffered-file) + 428 (flush _test-output-buffered-file) + 429 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- + 435 # check output + 436 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0") + 437 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-function-skeleton/1") + 438 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-function-skeleton/2") + 439 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3") + 440 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-function-skeleton/4") + 441 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5") + 442 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-function-skeleton/6") + 443 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-function-skeleton/7") + 444 # . epilogue + 445 89/<- %esp 5/r32/ebp + 446 5d/pop-to-ebp + 447 c3/return + 448 + 449 parse-mu: # in : (address buffered-file) + 450 # pseudocode + 451 # var curr-function = Program + 452 # var line : (stream byte 512) + 453 # var word-slice : slice + 454 # while true # line loop + 455 # clear-stream(line) + 456 # read-line-buffered(in, line) + 457 # if (line->write == 0) break # end of file + 458 # while true # word loop + 459 # word-slice = next-word-or-string(line) + 460 # if slice-empty?(word-slice) # end of line + 461 # break + 462 # else if slice-starts-with?(word-slice, "#") # comment + 463 # break # end of line + 464 # else if slice-equal(word-slice, "fn") + 465 # var new-function : (address function) = new function + 466 # populate-mu-function(in, new-function) + 467 # *curr-function = new-function + 468 # curr-function = &new-function->next + 469 # else + 470 # abort() + 471 # + 472 # . prologue + 473 55/push-ebp + 474 89/<- %ebp 4/r32/esp + 475 # . save registers + 476 50/push-eax + 477 51/push-ecx + 478 52/push-edx + 479 57/push-edi + 480 # var line/ecx : (stream byte 512) + 481 81 5/subop/subtract %esp 0x200/imm32 + 482 68/push 0x200/imm32/length + 483 68/push 0/imm32/read + 484 68/push 0/imm32/write + 485 89/<- %ecx 4/r32/esp + 486 # var word-slice/edx : slice + 487 68/push 0/imm32/end + 488 68/push 0/imm32/start + 489 89/<- %edx 4/r32/esp + 490 # var curr-function/edi : (address function) = Program + 491 bf/copy-to-edi Program/imm32 + 492 { + 493 $parse-mu:line-loop: + 494 (clear-stream %ecx) + 495 (read-line-buffered *(ebp+8) %ecx) + 496 # if (line->write == 0) break + 497 81 7/subop/compare *ecx 0/imm32 + 498 0f 84/jump-if-equal break/disp32 + 499 +-- 6 lines: #? # dump line --------------------------------------------------------------------------------------------------------------------------- + 505 { # word loop + 506 $parse-mu:word-loop: + 507 (next-word-or-string %ecx %edx) + 508 # if slice-empty?(word-slice) break + 509 (slice-empty? %edx) + 510 3d/compare-eax-and 0/imm32 + 511 0f 85/jump-if-not-equal break/disp32 + 512 # if (*word-slice->start == "#") break + 513 # . eax = *word-slice->start + 514 8b/-> *edx 0/r32/eax + 515 8a/copy-byte *eax 0/r32/AL + 516 81 4/subop/and %eax 0xff/imm32 + 517 # . if (eax == '#') break + 518 3d/compare-eax-and 0x23/imm32/hash + 519 0f 84/jump-if-equal break/disp32 + 520 # if (slice-equal?(word-slice, "fn")) parse a function + 521 { + 522 (slice-equal? %edx "fn") + 523 3d/compare-eax-and 0/imm32 + 524 0f 84/jump-if-equal break/disp32 + 525 # var new-function/eax : (address function) = populate-mu-function() + 526 (allocate Heap *Function-size) # => eax + 527 (populate-mu-function-header %ecx %eax) + 528 (populate-mu-function-body *(ebp+8) %eax) + 529 # *curr-function = new-function + 530 89/<- *edi 0/r32/eax + 531 # curr-function = &new-function->next + 532 8d/address-> *(eax+0x10) 7/r32/edi + 533 e9/jump $parse-mu:word-loop/disp32 + 534 } + 535 # otherwise abort + 536 e9/jump $parse-mu:abort/disp32 + 537 } # end word loop + 538 e9/jump loop/disp32 + 539 } # end line loop + 540 $parse-mu:end: + 541 # . reclaim locals + 542 81 0/subop/add %esp 0x214/imm32 + 543 # . restore registers + 544 5f/pop-to-edi + 545 5a/pop-to-edx + 546 59/pop-to-ecx + 547 58/pop-to-eax + 548 # . epilogue + 549 89/<- %esp 5/r32/ebp + 550 5d/pop-to-ebp + 551 c3/return + 552 + 553 $parse-mu:abort: + 554 # error("unexpected top-level command: " word-slice "\n") + 555 (write-buffered Stderr "unexpected top-level command: ") + 556 (write-buffered Stderr %edx) + 557 (write-buffered Stderr "\n") + 558 (flush Stderr) + 559 # . syscall(exit, 1) + 560 bb/copy-to-ebx 1/imm32 + 561 b8/copy-to-eax 1/imm32/exit + 562 cd/syscall 0x80/imm8 + 563 # never gets here + 564 + 565 # errors considered: + 566 # fn foo { { + 567 # fn foo { } + 568 # fn foo { } { + 569 # fn foo # no block + 570 populate-mu-function-header: # first-line : (address stream byte), out : (address function) + 571 # . prologue + 572 55/push-ebp + 573 89/<- %ebp 4/r32/esp + 574 # . save registers + 575 50/push-eax + 576 51/push-ecx + 577 57/push-edi + 578 # edi = out + 579 8b/-> *(ebp+0xc) 7/r32/edi + 580 # var word-slice/ecx : slice + 581 68/push 0/imm32/end + 582 68/push 0/imm32/start + 583 89/<- %ecx 4/r32/esp + 584 # save function name + 585 (next-word *(ebp+8) %ecx) + 586 (slice-to-string Heap %ecx) # => eax + 587 89/<- *edi 0/r32/eax + 588 # assert that next token is '{' + 589 (next-word *(ebp+8) %ecx) + 590 (slice-equal? %ecx "{") + 591 3d/compare-eax-and 0/imm32 + 592 74/jump-if-equal $populate-mu-function-header:abort/disp8 + 593 # assert that there's no further token + 594 { + 595 # word-slice = next-word(line) + 596 (next-word *(ebp+8) %ecx) + 597 # if (word-slice == '') break + 598 (slice-empty? %ecx) + 599 3d/compare-eax-and 0/imm32 + 600 75/jump-if-not-equal break/disp8 + 601 # if (slice-starts-with?(word-slice, "#")) break + 602 # . eax = *word-slice->start + 603 8b/-> *edx 0/r32/eax + 604 8a/copy-byte *eax 0/r32/AL + 605 81 4/subop/and %eax 0xff/imm32 + 606 # . if (eax == '#') break + 607 3d/compare-eax-and 0x23/imm32/hash + 608 74/jump-if-equal break/disp8 + 609 # otherwise abort + 610 eb/jump $populate-mu-function-header:abort/disp8 + 611 } + 612 $populate-mu-function-header:end: + 613 # . reclaim locals + 614 81 0/subop/add %esp 8/imm32 + 615 # . restore registers + 616 5f/pop-to-edi + 617 59/pop-to-ecx + 618 58/pop-to-eax + 619 # . epilogue + 620 89/<- %esp 5/r32/ebp + 621 5d/pop-to-ebp + 622 c3/return + 623 + 624 $populate-mu-function-header:abort: + 625 # error("function header not in form 'fn <name> {'") + 626 (write-buffered Stderr "function header not in form 'fn <name> {' -- '") + 627 (rewind-stream *(ebp+8)) + 628 (write-stream 2 *(ebp+8)) + 629 (write-buffered Stderr "'\n") + 630 (flush Stderr) + 631 # . syscall(exit, 1) + 632 bb/copy-to-ebx 1/imm32 + 633 b8/copy-to-eax 1/imm32/exit + 634 cd/syscall 0x80/imm8 + 635 # never gets here + 636 + 637 # errors considered: + 638 # { abc + 639 populate-mu-function-body: # in : (address buffered-file), out : (address function) + 640 # . prologue + 641 55/push-ebp + 642 89/<- %ebp 4/r32/esp + 643 # . save registers + 644 50/push-eax + 645 51/push-ecx + 646 52/push-edx + 647 53/push-ebx + 648 # var line/ecx : (stream byte 512) + 649 81 5/subop/subtract %esp 0x200/imm32 + 650 68/push 0x200/imm32/length + 651 68/push 0/imm32/read + 652 68/push 0/imm32/write + 653 89/<- %ecx 4/r32/esp + 654 # var word-slice/edx : slice + 655 68/push 0/imm32/end + 656 68/push 0/imm32/start + 657 89/<- %edx 4/r32/esp + 658 # var open-curly-count/ebx : int = 1 + 659 bb/copy-to-ebx 1/imm32 + 660 { # line loop + 661 $populate-mu-function-body:line-loop: + 662 # if (open-curly-count == 0) break + 663 81 7/subop/compare %ebx 0/imm32 + 664 0f 84/jump-if-equal break/disp32 + 665 # line = read-line-buffered(in) + 666 (clear-stream %ecx) + 667 (read-line-buffered *(ebp+8) %ecx) + 668 # if (line->write == 0) break + 669 81 7/subop/compare *ecx 0/imm32 + 670 0f 84/jump-if-equal break/disp32 + 671 # word-slice = next-word(line) + 672 (next-word %ecx %edx) + 673 # if slice-empty?(word-slice) continue + 674 (slice-empty? %ecx) + 675 3d/compare-eax-and 0/imm32 + 676 75/jump-if-not-equal loop/disp8 + 677 # if (slice-starts-with?(word-slice, '#') continue + 678 # . eax = *word-slice->start + 679 8b/-> *edx 0/r32/eax + 680 8a/copy-byte *eax 0/r32/AL + 681 81 4/subop/and %eax 0xff/imm32 + 682 # . if (eax == '#') continue + 683 3d/compare-eax-and 0x23/imm32/hash + 684 74/jump-if-equal loop/disp8 + 685 { + 686 # if slice-equal?(word-slice, "{") ++open-curly-count + 687 { + 688 (slice-equal? %ecx "{") + 689 3d/compare-eax-and 0/imm32 + 690 74/jump-if-equal break/disp8 + 691 43/increment-ebx + 692 eb/jump $curly-found:end/disp8 + 693 } + 694 # else if slice-equal?(word-slice, "}") --open-curly-count + 695 { + 696 (slice-equal? %ecx "}") + 697 3d/compare-eax-and 0/imm32 + 698 74/jump-if-equal break/disp8 + 699 4b/decrement-ebx + 700 eb/jump $curly-found:end/disp8 + 701 } + 702 # else break + 703 eb/jump $populate-mu-function-body:end/disp8 + 704 } + 705 # - check for invalid tokens after curly + 706 $curly-found:end: + 707 # second-word-slice = next-word(line) + 708 (next-word %ecx %edx) + 709 # if slice-empty?(second-word-slice) continue + 710 (slice-empty? %ecx) + 711 3d/compare-eax-and 0/imm32 + 712 0f 85/jump-if-not-equal loop/disp32 + 713 # if (slice-starts-with?(second-word-slice, '#') continue + 714 # . eax = *second-word-slice->start + 715 8b/-> *edx 0/r32/eax + 716 8a/copy-byte *eax 0/r32/AL + 717 81 4/subop/and %eax 0xff/imm32 + 718 # . if (eax == '#') continue + 719 3d/compare-eax-and 0x23/imm32/hash + 720 0f 84/jump-if-equal loop/disp32 + 721 # abort + 722 eb/jump $populate-mu-function-body:abort/disp8 + 723 } # end line loop + 724 $populate-mu-function-body:end: + 725 # . reclaim locals + 726 81 0/subop/add %esp 0x214/imm32 + 727 # . restore registers + 728 5b/pop-to-ebx + 729 5a/pop-to-edx + 730 59/pop-to-ecx + 731 58/pop-to-eax + 732 # . epilogue + 733 89/<- %esp 5/r32/ebp + 734 5d/pop-to-ebp + 735 c3/return + 736 + 737 $populate-mu-function-body:abort: + 738 # error("'{' or '}' should be on its own line, but got '") + 739 (write-buffered Stderr "'{' or '}' should be on its own line, but got '") + 740 (rewind-stream %ecx) + 741 (write-stream 2 %ecx) + 742 (write-buffered Stderr "'\n") + 743 (flush Stderr) + 744 # . syscall(exit, 1) + 745 bb/copy-to-ebx 1/imm32 + 746 b8/copy-to-eax 1/imm32/exit + 747 cd/syscall 0x80/imm8 + 748 # never gets here + 749 + 750 check-mu-types: + 751 # . prologue + 752 55/push-ebp + 753 89/<- %ebp 4/r32/esp + 754 # + 755 $check-types:end: + 756 # . epilogue + 757 89/<- %esp 5/r32/ebp + 758 5d/pop-to-ebp + 759 c3/return + 760 + 761 emit-subx: # out : (address buffered-file) + 762 # . prologue + 763 55/push-ebp + 764 89/<- %ebp 4/r32/esp + 765 # . save registers + 766 50/push-eax + 767 51/push-ecx + 768 57/push-edi + 769 # edi = out + 770 8b/-> *(ebp+8) 7/r32/edi + 771 # var curr/ecx : (address function) = Program + 772 8b/-> *Program 1/r32/ecx + 773 { + 774 # if (curr == NULL) break + 775 81 7/subop/compare %ecx 0/imm32 + 776 0f 84/jump-if-equal break/disp32 + 777 (emit-subx-function %edi %ecx) + 778 # curr = curr->next + 779 8b/-> *(ecx+0x10) 1/r32/ecx + 780 e9/jump loop/disp32 + 781 } + 782 $emit-subx:end: + 783 # . restore registers + 784 5f/pop-to-edi + 785 59/pop-to-ecx + 786 58/pop-to-eax 787 # . epilogue 788 89/<- %esp 5/r32/ebp 789 5d/pop-to-ebp 790 c3/return 791 - 792 emit-subx-statement: # out : (address buffered-file), stmt : (address statement), vars : (address variable), regs : (address array (address variable)), primitives : (address opcode-info), functions : (address function) - 793 # . prologue - 794 55/push-ebp - 795 89/<- %ebp 4/r32/esp - 796 # . save registers - 797 50/push-eax - 798 51/push-ecx - 799 # var curr/ecx : (address primitive) = primitives - 800 8b/-> *(ebp+0x18) 1/r32/ecx - 801 { - 802 # if (curr != null) abort - 803 81 7/subop/compare *(ebp+0xc) 0/imm32 - 804 0f 84/jump-if-equal $emit-subx-statement:abort/disp32 - 805 # if (match(curr, stmt)) break - 806 (mu-stmt-matches-primitive? *(ebp+0xc) %ecx) # => eax - 807 3d/compare-eax-and 0/imm32 - 808 75/jump-if-not-equal break/disp8 - 809 # emit code for stmt according to curr and vars - 810 # curr = curr->next - 811 8b/-> *(ecx+0x10) 1/r32/ecx - 812 e9/jump loop/disp32 - 813 } - 814 $emit-subx-statement:end: - 815 # . restore registers - 816 59/pop-to-ecx - 817 58/pop-to-eax - 818 # . epilogue - 819 89/<- %esp 5/r32/ebp - 820 5d/pop-to-ebp - 821 c3/return - 822 - 823 $emit-subx-statement:abort: - 824 # error("couldn't translate '" stmt "'\n") - 825 (write-buffered Stderr "couldn't translate '") - 826 #? (emit-string Stderr *(ebp+0xc)) # TODO - 827 (write-buffered Stderr "'\n") - 828 (flush Stderr) - 829 # . syscall(exit, 1) - 830 bb/copy-to-ebx 1/imm32 - 831 b8/copy-to-eax 1/imm32/exit - 832 cd/syscall 0x80/imm8 - 833 # never gets here - 834 - 835 mu-stmt-matches-primitive?: # stmt : (address statement), primitive : (address opcode-info) => result/eax : boolean - 836 # . prologue - 837 55/push-ebp - 838 89/<- %ebp 4/r32/esp - 839 # . save registers - 840 51/push-ecx - 841 # return primitive->name == stmt->operation - 842 8b/-> *(ebp+8) 1/r32/ecx - 843 8b/-> *(ebp+0xc) 0/r32/eax - 844 (string-equal? *ecx *eax) # => eax - 845 $mu-stmt-matches-primitive?:end: - 846 # . restore registers - 847 59/pop-to-ecx - 848 # . epilogue - 849 89/<- %esp 5/r32/ebp - 850 5d/pop-to-ebp - 851 c3/return - 852 - 853 test-emit-subx-statement-primitive: - 854 # Primitive operation on a variable on the stack. - 855 # increment foo - 856 # => - 857 # ff 0/subop/increment *(ebp-8) - 858 # - 859 # There's a variable on the var stack as follows: - 860 # name: 'foo' - 861 # type: int - 862 # location: -8 (negative numbers are on the stack; - 863 # 0-7 are in registers; - 864 # higher positive numbers are invalid) - 865 # - 866 # There's nothing in registers. - 867 # - 868 # There's a primitive with this info: - 869 # name: 'increment' - 870 # inout: int/mem - 871 # value: 'ff 0/subop/increment' - 872 # - 873 # There's nothing in functions. - 874 # - 875 # . prologue - 876 55/push-ebp - 877 89/<- %ebp 4/r32/esp - 878 # setup - 879 (clear-stream _test-output-stream) - 880 (clear-stream _test-output-buffered-file->buffer) - 881 # . ecx = vars - 882 68/push 0/imm32/next - 883 68/push -8/imm32/stack-offset - 884 68/push 0/imm32/int # TODO - 885 68/push "foo"/imm32 - 886 89/<- %ecx 4/r32/esp - 887 # . edx = operand - 888 68/push 0/imm32/next - 889 51/push-ecx/var-foo - 890 89/<- %edx 4/r32/esp - 891 # . edx = stmt - 892 68/push 0/imm32/next - 893 68/push 0/imm32/outputs - 894 52/push-edx/operand - 895 68/push "increment"/imm32/operation - 896 89/<- %edx 4/r32/esp - 897 # . ebx = primitives - 898 68/push 0/imm32/next - 899 68/push "ff 0/subop/increment"/imm32 - 900 68/push 0/imm32/type-int - 901 68/push 0/imm32/storage-memory - 902 68/push "increment"/imm32/name - 903 89/<- %ebx 4/r32/esp - 904 # convert - 905 (emit-subx-statement _test-output-buffered-file %edx %ecx 0 %ebx 0) - 906 (flush _test-output-buffered-file) - 907 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- - 913 # check output - 914 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp-8)" "F - test-emit-subx-statement-primitive/0") - 915 # . reclaim locals - 916 81 0/subop/add %esp 0x3c/imm32 - 917 # . epilogue - 918 89/<- %esp 5/r32/ebp - 919 5d/pop-to-ebp - 920 c3/return - 921 - 922 test-emit-subx-statement-function-call: - 923 # Call a function on a variable on the stack. - 924 # f var - 925 # => - 926 # (f2 *(ebp-8)) - 927 # (Changing the function name just to help disambiguate things.) - 928 # - 929 # There's a variable on the var stack as follows: - 930 # name: 'var' - 931 # type: int - 932 # location: -8 (negative numbers are on the stack; - 933 # 0-7 are in registers; - 934 # higher positive numbers are invalid) - 935 # - 936 # There's nothing in registers. - 937 # - 938 # There's nothing in primitives. - 939 # - 940 # There's a function with this info: - 941 # name: 'f' - 942 # inout: int/mem - 943 # value: 'f2' - 944 # - 945 # . prologue - 946 55/push-ebp - 947 89/<- %ebp 4/r32/esp - 948 # setup - 949 (clear-stream _test-output-stream) - 950 (clear-stream _test-output-buffered-file->buffer) - 951 # . ecx = vars - 952 68/push 0/imm32/next - 953 68/push -8/imm32/stack-offset - 954 68/push 0/imm32/int # TODO - 955 68/push "var"/imm32 - 956 89/<- %ecx 4/r32/esp - 957 # . edx = operand - 958 68/push 0/imm32/next - 959 51/push-ecx/var - 960 89/<- %edx 4/r32/esp - 961 # . edx = stmt - 962 68/push 0/imm32/next - 963 68/push 0/imm32/outputs - 964 52/push-edx/operand - 965 68/push "f"/imm32/operation - 966 89/<- %edx 4/r32/esp - 967 # . ebx = functions - 968 68/push 0/imm32/next - 969 68/push "f2"/imm32 - 970 68/push 0/imm32/type-int - 971 68/push 0/imm32/storage-memory - 972 68/push "f"/imm32/name - 973 89/<- %ebx 4/r32/esp - 974 # convert - 975 (emit-subx-statement _test-output-buffered-file %edx %ecx 0 0 %ebx) - 976 (flush _test-output-buffered-file) - 977 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- - 983 # check output - 984 (check-next-stream-line-equal _test-output-stream "f2 *(ebp-8)" "F - test-emit-subx-statement-function-call/0") - 985 # . reclaim locals - 986 81 0/subop/add %esp 0x3c/imm32 - 987 # . epilogue - 988 89/<- %esp 5/r32/ebp - 989 5d/pop-to-ebp - 990 c3/return - 991 - 992 emit-subx-prologue: # out : (address buffered-file) - 993 # . prologue - 994 55/push-ebp - 995 89/<- %ebp 4/r32/esp - 996 # - 997 (write-buffered *(ebp+8) "# . prologue\n") - 998 (write-buffered *(ebp+8) "55/push-ebp\n") - 999 (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n") -1000 $emit-subx-prologue:end: -1001 # . epilogue -1002 89/<- %esp 5/r32/ebp -1003 5d/pop-to-ebp -1004 c3/return -1005 -1006 emit-subx-epilogue: # out : (address buffered-file) -1007 # . prologue -1008 55/push-ebp -1009 89/<- %ebp 4/r32/esp -1010 # -1011 (write-buffered *(ebp+8) "# . epilogue\n") -1012 (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n") -1013 (write-buffered *(ebp+8) "5d/pop-to-ebp\n") -1014 (write-buffered *(ebp+8) "c3/return\n") -1015 $emit-subx-epilogue:end: -1016 # . epilogue -1017 89/<- %esp 5/r32/ebp -1018 5d/pop-to-ebp -1019 c3/return + 792 # == Emitting a function + 793 # Emit function header + 794 # Emit function prologue + 795 # Translate function body + 796 # Emit function epilogue + 797 + 798 emit-subx-function: # out : (address buffered-file), f : (address function) + 799 # . prologue + 800 55/push-ebp + 801 89/<- %ebp 4/r32/esp + 802 # . save registers + 803 50/push-eax + 804 51/push-ecx + 805 57/push-edi + 806 # edi = out + 807 8b/-> *(ebp+8) 7/r32/edi + 808 # ecx = f + 809 8b/-> *(ebp+0xc) 1/r32/ecx + 810 # + 811 (write-buffered %edi *ecx) + 812 (write-buffered %edi ":\n") + 813 (emit-subx-prologue %edi) + 814 (emit-subx-block %edi *(ecx+4)) # TODO: offset + 815 (emit-subx-epilogue %edi) + 816 $emit-subx-function:end: + 817 # . restore registers + 818 5f/pop-to-edi + 819 59/pop-to-ecx + 820 58/pop-to-eax + 821 # . epilogue + 822 89/<- %esp 5/r32/ebp + 823 5d/pop-to-ebp + 824 c3/return + 825 + 826 emit-subx-block: # out : (address buffered-file), block : (address block) + 827 # . prologue + 828 55/push-ebp + 829 89/<- %ebp 4/r32/esp + 830 # + 831 $emit-subx-block:end: + 832 # . epilogue + 833 89/<- %esp 5/r32/ebp + 834 5d/pop-to-ebp + 835 c3/return + 836 + 837 emit-subx-statement: # out : (address buffered-file), stmt : (address statement), vars : (stack var), primitives : (address opcode-info), functions : (address function) + 838 # . prologue + 839 55/push-ebp + 840 89/<- %ebp 4/r32/esp + 841 # . save registers + 842 50/push-eax + 843 51/push-ecx + 844 # if stmt matches a primitive, emit it + 845 { + 846 (find-matching-function *(ebp+0x14) *(ebp+0xc)) + 847 3d/compare-eax-and 0/imm32 + 848 74/jump-if-equal break/disp8 + 849 (emit-subx-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) + 850 e9/jump $emit-subx-statement:end/disp32 + 851 } + 852 # else if stmt matches a function, emit a call to it + 853 { + 854 (find-matching-function *(ebp+0x18) *(ebp+0xc)) + 855 3d/compare-eax-and 0/imm32 + 856 74/jump-if-equal break/disp8 + 857 (emit-subx-call *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) + 858 e9/jump $emit-subx-statement:end/disp32 + 859 } + 860 # else abort + 861 e9/jump $emit-subx-statement:abort/disp32 + 862 $emit-subx-statement:end: + 863 # . restore registers + 864 59/pop-to-ecx + 865 58/pop-to-eax + 866 # . epilogue + 867 89/<- %esp 5/r32/ebp + 868 5d/pop-to-ebp + 869 c3/return + 870 + 871 $emit-subx-statement:abort: + 872 # error("couldn't translate '" stmt "'\n") + 873 (write-buffered Stderr "couldn't translate '") + 874 #? (emit-string Stderr *(ebp+0xc)) # TODO + 875 (write-buffered Stderr "'\n") + 876 (flush Stderr) + 877 # . syscall(exit, 1) + 878 bb/copy-to-ebx 1/imm32 + 879 b8/copy-to-eax 1/imm32/exit + 880 cd/syscall 0x80/imm8 + 881 # never gets here + 882 + 883 emit-subx-primitive: # out : (address buffered-file), stmt : (address statement), vars : (address variable), primitive : (address function) + 884 # . prologue + 885 55/push-ebp + 886 89/<- %ebp 4/r32/esp + 887 # . save registers + 888 50/push-eax + 889 51/push-ecx + 890 # - emit primitive name + 891 8b/-> *(ebp+0x14) 1/r32/ecx + 892 (write-buffered *(ebp+8) *(ecx+4)) # Function-subx-name + 893 # - emit arguments + 894 # var curr/ecx : (list var) = stmt->inouts + 895 8b/-> *(ebp+0xc) 1/r32/ecx + 896 8b/-> *(ecx+4) 1/r32/ecx # Stmt-inouts + 897 { + 898 # if (curr == null) break + 899 81 7/subop/compare %ecx 0/imm32 + 900 74/jump-if-equal break/disp8 + 901 # + 902 (emit-subx-call-operand *(ebp+8) *ecx) + 903 # curr = curr->next + 904 8b/-> *(ecx+4) 1/r32/ecx + 905 } + 906 $emit-subx-primitive:end: + 907 # . restore registers + 908 59/pop-to-ecx + 909 58/pop-to-eax + 910 # . epilogue + 911 89/<- %esp 5/r32/ebp + 912 5d/pop-to-ebp + 913 c3/return + 914 + 915 emit-subx-call: # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function) + 916 # . prologue + 917 55/push-ebp + 918 89/<- %ebp 4/r32/esp + 919 # . save registers + 920 50/push-eax + 921 51/push-ecx + 922 # + 923 (write-buffered *(ebp+8) "(") + 924 # - emit function name + 925 8b/-> *(ebp+0x14) 1/r32/ecx + 926 (write-buffered *(ebp+8) *(ecx+4)) # Function-subx-name + 927 # - emit arguments + 928 # var curr/ecx : (list var) = stmt->inouts + 929 8b/-> *(ebp+0xc) 1/r32/ecx + 930 8b/-> *(ecx+4) 1/r32/ecx # Stmt-inouts + 931 { + 932 # if (curr == null) break + 933 81 7/subop/compare %ecx 0/imm32 + 934 74/jump-if-equal break/disp8 + 935 # + 936 (emit-subx-call-operand *(ebp+8) *ecx) + 937 # curr = curr->next + 938 8b/-> *(ecx+4) 1/r32/ecx + 939 } + 940 # + 941 (write-buffered *(ebp+8) ")") + 942 $emit-subx-call:end: + 943 # . restore registers + 944 59/pop-to-ecx + 945 58/pop-to-eax + 946 # . epilogue + 947 89/<- %esp 5/r32/ebp + 948 5d/pop-to-ebp + 949 c3/return + 950 + 951 emit-subx-call-operand: # out : (address buffered-file), operand : (address variable) + 952 # . prologue + 953 55/push-ebp + 954 89/<- %ebp 4/r32/esp + 955 # . save registers + 956 50/push-eax + 957 # + 958 (write-buffered *(ebp+8) Space) + 959 (write-buffered *(ebp+8) "*(ebp+") + 960 8b/-> *(ebp+0xc) 0/r32/eax + 961 (print-int32-buffered *(ebp+8) *(eax+8)) # Var-location + 962 (write-buffered *(ebp+8) ")") + 963 $emit-subx-call-operand:end: + 964 # . restore registers + 965 58/pop-to-eax + 966 # . epilogue + 967 89/<- %esp 5/r32/ebp + 968 5d/pop-to-ebp + 969 c3/return + 970 + 971 find-matching-function: # functions : (address function), stmt : (address statement) -> result/eax : (address function) + 972 # . prologue + 973 55/push-ebp + 974 89/<- %ebp 4/r32/esp + 975 # . save registers + 976 51/push-ecx + 977 # var curr/ecx : (address function) = functions + 978 8b/-> *(ebp+8) 1/r32/ecx + 979 { + 980 # if (curr == null) break + 981 81 7/subop/compare %ecx 0/imm32 + 982 74/jump-if-equal break/disp8 + 983 # if match(curr, stmt) return curr + 984 { + 985 (mu-stmt-matches-function? *(ebp+0xc) %ecx) # => eax + 986 3d/compare-eax-and 0/imm32 + 987 74/jump-if-equal break/disp8 + 988 89/<- %eax 1/r32/ecx + 989 eb/jump $find-matching-function:end/disp8 + 990 } + 991 # curr = curr->next + 992 8b/-> *(ecx+0x10) 1/r32/ecx + 993 eb/jump loop/disp8 + 994 } + 995 # return null + 996 b8/copy-to-eax 0/imm32 + 997 $find-matching-function:end: + 998 # . restore registers + 999 59/pop-to-ecx +1000 # . epilogue +1001 89/<- %esp 5/r32/ebp +1002 5d/pop-to-ebp +1003 c3/return +1004 +1005 mu-stmt-matches-function?: # stmt : (address statement), primitive : (address opcode-info) => result/eax : boolean +1006 # . prologue +1007 55/push-ebp +1008 89/<- %ebp 4/r32/esp +1009 # . save registers +1010 51/push-ecx +1011 # return primitive->name == stmt->operation +1012 8b/-> *(ebp+8) 1/r32/ecx +1013 8b/-> *(ebp+0xc) 0/r32/eax +1014 (string-equal? *ecx *eax) # => eax +1015 $mu-stmt-matches-function?:end: +1016 # . restore registers +1017 59/pop-to-ecx +1018 # . epilogue +1019 89/<- %esp 5/r32/ebp +1020 5d/pop-to-ebp +1021 c3/return +1022 +1023 test-emit-subx-statement-primitive: +1024 # Primitive operation on a variable on the stack. +1025 # increment foo +1026 # => +1027 # ff 0/subop/increment *(ebp-8) +1028 # +1029 # There's a variable on the var stack as follows: +1030 # name: 'foo' +1031 # type: int +1032 # location: -8 (negative numbers are on the stack; +1033 # 0-7 are in registers; +1034 # higher positive numbers are invalid) +1035 # +1036 # There's nothing in registers. +1037 # +1038 # There's a primitive with this info: +1039 # name: 'increment' +1040 # inout: int/mem +1041 # value: 'ff 0/subop/increment' +1042 # +1043 # There's nothing in functions. +1044 # +1045 # . prologue +1046 55/push-ebp +1047 89/<- %ebp 4/r32/esp +1048 # setup +1049 (clear-stream _test-output-stream) +1050 (clear-stream _test-output-buffered-file->buffer) +1051 # var-foo/ecx : var +1052 68/push 1/imm32/block-depth +1053 68/push -8/imm32/location +1054 68/push 1/imm32/type-int +1055 68/push "foo"/imm32 +1056 89/<- %ecx 4/r32/esp +1057 # vars/edx : (stack 1) +1058 51/push-ecx/var-foo +1059 68/push 1/imm32/data-length +1060 68/push 1/imm32/top +1061 89/<- %edx 4/r32/esp +1062 # operand/esi : (list var) +1063 68/push 0/imm32/next +1064 51/push-ecx/var-foo +1065 89/<- %esi 4/r32/esp +1066 # stmt/esi : statement +1067 68/push 0/imm32/next +1068 68/push 0/imm32/outputs +1069 56/push-esi/operands +1070 68/push "increment"/imm32/operation +1071 89/<- %esi 4/r32/esp +1072 # primitives/ebx : function +1073 68/push 0/imm32/next +1074 68/push 0/imm32/body +1075 68/push 0/imm32/outputs +1076 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call +1077 68/push "ff 0/subop/increment"/imm32/subx-name +1078 68/push "increment"/imm32/name +1079 89/<- %ebx 4/r32/esp +1080 # convert +1081 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) +1082 (flush _test-output-buffered-file) +1083 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- +1089 # check output +1090 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive/0") +1091 # . reclaim locals +1092 81 0/subop/add %esp 0x48/imm32 +1093 # . epilogue +1094 89/<- %esp 5/r32/ebp +1095 5d/pop-to-ebp +1096 c3/return +1097 +1098 test-emit-subx-statement-function-call: +1099 # Call a function on a variable on the stack. +1100 # f foo +1101 # => +1102 # (f2 *(ebp-8)) +1103 # (Changing the function name supports overloading in general, but here it +1104 # just serves to help disambiguate things.) +1105 # +1106 # There's a variable on the var stack as follows: +1107 # name: 'foo' +1108 # type: int +1109 # location: -8 (negative numbers are on the stack; +1110 # 0-7 are in registers; +1111 # higher positive numbers are invalid) +1112 # +1113 # There's nothing in registers. +1114 # +1115 # There's nothing in primitives. +1116 # +1117 # There's a function with this info: +1118 # name: 'f' +1119 # inout: int/mem +1120 # value: 'f2' +1121 # +1122 # . prologue +1123 55/push-ebp +1124 89/<- %ebp 4/r32/esp +1125 # setup +1126 (clear-stream _test-output-stream) +1127 (clear-stream _test-output-buffered-file->buffer) +1128 # var-foo/ecx : var +1129 68/push 0/imm32/block-depth +1130 68/push -8/imm32/location +1131 68/push 1/imm32/type-int +1132 68/push "foo"/imm32 +1133 89/<- %ecx 4/r32/esp +1134 # vars/edx = (stack 1) +1135 51/push-ecx/var-foo +1136 68/push 1/imm32/data-length +1137 68/push 1/imm32/top +1138 89/<- %edx 4/r32/esp +1139 # operands/esi : (list var) +1140 68/push 0/imm32/next +1141 51/push-ecx/var-foo +1142 89/<- %esi 4/r32/esp +1143 # stmt/esi : statement +1144 68/push 0/imm32/next +1145 68/push 0/imm32/outputs +1146 56/push-esi/inouts +1147 68/push "f"/imm32/operation +1148 89/<- %esi 4/r32/esp +1149 # functions/ebx : function +1150 68/push 0/imm32/next +1151 68/push 0/imm32/body +1152 68/push 0/imm32/outputs +1153 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call +1154 68/push "f2"/imm32/subx-name +1155 68/push "f"/imm32/name +1156 89/<- %ebx 4/r32/esp +1157 # convert +1158 (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx) +1159 (flush _test-output-buffered-file) +1160 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- +1166 # check output +1167 (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call/0") +1168 # . reclaim locals +1169 81 0/subop/add %esp 0x3c/imm32 +1170 # . epilogue +1171 89/<- %esp 5/r32/ebp +1172 5d/pop-to-ebp +1173 c3/return +1174 +1175 emit-subx-prologue: # out : (address buffered-file) +1176 # . prologue +1177 55/push-ebp +1178 89/<- %ebp 4/r32/esp +1179 # +1180 (write-buffered *(ebp+8) "# . prologue\n") +1181 (write-buffered *(ebp+8) "55/push-ebp\n") +1182 (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n") +1183 $emit-subx-prologue:end: +1184 # . epilogue +1185 89/<- %esp 5/r32/ebp +1186 5d/pop-to-ebp +1187 c3/return +1188 +1189 emit-subx-epilogue: # out : (address buffered-file) +1190 # . prologue +1191 55/push-ebp +1192 89/<- %ebp 4/r32/esp +1193 # +1194 (write-buffered *(ebp+8) "# . epilogue\n") +1195 (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n") +1196 (write-buffered *(ebp+8) "5d/pop-to-ebp\n") +1197 (write-buffered *(ebp+8) "c3/return\n") +1198 $emit-subx-epilogue:end: +1199 # . epilogue +1200 89/<- %esp 5/r32/ebp +1201 5d/pop-to-ebp +1202 c3/return -- cgit 1.4.1-2-gfad0