From 4d20c9ee94436b527a5215b5299ffce06f08d371 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 26 Nov 2019 17:41:25 -0800 Subject: 5761 --- apps/mu | Bin 52662 -> 52835 bytes apps/mu.subx | 45 +- html/apps/mu.subx.html | 4328 +++++++++++++++++++++++++----------------------- test_apps | 2 +- 4 files changed, 2273 insertions(+), 2102 deletions(-) diff --git a/apps/mu b/apps/mu index a29f4063..c014f5ee 100755 Binary files a/apps/mu and b/apps/mu differ diff --git a/apps/mu.subx b/apps/mu.subx index 27f7d7dc..f0f557cd 100644 --- a/apps/mu.subx +++ b/apps/mu.subx @@ -1052,6 +1052,26 @@ _Primitive-add-reg-to-reg: 3/imm32/rm32-is-first-output 1/imm32/r32-is-first-inout 0/imm32/no-imm32 + _Primitive-add-reg-to-mem/imm32/next +_Primitive-add-reg-to-mem: + # add-to var1 var2/reg => 01 var1 var2/r32 + "add-to"/imm32/name + Int-var-and-second-int-var-in-some-register/imm32/inouts + 0/imm32/outputs + "01"/imm32/subx-name + 1/imm32/rm32-is-first-inout + 2/imm32/r32-is-second-inout + 0/imm32/no-imm32 + _Primitive-add-mem-to-reg/imm32/next +_Primitive-add-mem-to-reg: + # var1/reg <- add var2 => 03 var2/rm32 var1/r32 + "add"/imm32/name + Single-int-var-on-stack/imm32/inouts + Single-int-var-in-some-register/imm32/outputs + "03"/imm32/subx-name + 1/imm32/rm32-is-first-inout + 3/imm32/r32-is-first-output + 0/imm32/no-imm32 _Primitive-add-lit-to-reg/imm32/next _Primitive-add-lit-to-reg: # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32 @@ -1062,6 +1082,16 @@ _Primitive-add-lit-to-reg: 3/imm32/rm32-is-first-output 0/imm32/no-r32 1/imm32/imm32-is-first-inout + _Primitive-add-lit-to-mem/imm32/next +_Primitive-add-lit-to-mem: + # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32 + "add"/imm32/name + Int-var-and-literal/imm32/inouts + 0/imm32/outputs + "81 0/subop/add"/imm32/subx-name + 1/imm32/rm32-is-first-inout + 0/imm32/no-r32 + 2/imm32/imm32-is-first-inout 0/imm32/next Single-int-var-on-stack: @@ -1075,6 +1105,14 @@ Int-var-on-stack: 1/imm32/some-stack-offset 0/imm32/no-register +Int-var-and-second-int-var-in-some-register: + Int-var-on-stack/imm32 + Int-var-in-some-register/imm32/next + +Int-var-and-literal: + Int-var-on-stack/imm32 + Single-lit-var/imm32/next + Single-int-var-in-some-register: Int-var-in-some-register/imm32 0/imm32/next @@ -1083,7 +1121,7 @@ Int-var-in-some-register: "arg1"/imm32/name 1/imm32/type-int 1/imm32/some-block-depth - 0/imm32/some-stack-offset + 0/imm32/no-stack-offset "*"/imm32/register Single-lit-var: @@ -1578,7 +1616,10 @@ operand-matches-primitive?: # var : (address var), primout-var : (address var) # edi = primout-var 8b/-> *(ebp+0xc) 7/r32/edi # if (var->type != primout-var->type) return false - # TODO + 8b/-> *(esi+4) 0/r32/eax # Var-type + 39/compare *(edi+4) 0/r32/eax # Var-type + b8/copy-to-eax 0/imm32/false + 75/jump-if-not-equal $operand-matches-primitive?:end/disp8 # return false if var->register doesn't match primout-var->register { # if addresses are equal, don't return here diff --git a/html/apps/mu.subx.html b/html/apps/mu.subx.html index a69e54fa..785e24f8 100644 --- a/html/apps/mu.subx.html +++ b/html/apps/mu.subx.html @@ -153,2116 +153,2246 @@ if ('onhashchange' in window) { 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 # operation: string - 98 # inouts: linked list of vars - 99 # outputs: linked list of vars - 100 # block = linked list of statements - 101 - 102 # == Translation: managing the stack - 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. One crucial piece of the - 105 # puzzle is how Mu will clean up variables defined on the stack for you. - 106 # - 107 # Assume that we maintain a 'functions' list while parsing source code. And a - 108 # 'primitives' list is a global constant. Both these contain enough information - 109 # to perform type-checking on function calls or primitive statements, respectively. - 110 # - 111 # Defining variables pushes them on a stack with the current block depth and - 112 # enough information about their location (stack offset or register). - 113 # Starting a block increments the current block id. - 114 # Each statement now has enough information to emit code for it. - 115 # Ending a block is where the magic happens: - 116 # pop all variables at the current block depth - 117 # emit code to restore all register variables introduced at the current depth - 118 # emit code to clean up all stack variables at the current depth (just increment esp) - 119 # decrement the current block depth - 120 # - 121 # Formal types: - 122 # live-vars: stack of vars - 123 # var: - 124 # name: string - 125 # type: s-expression? Just a type id for now. - 126 # block: int - 127 # stack-offset: int (added to ebp) - 128 # register: string - 129 # either usual register names - 130 # or '*' to indicate any register - 131 # At most one of stack-offset or register-index must be non-zero. - 132 # A register of '*' designates a variable _template_. Only legal in formal - 133 # parameters for primitives. - 134 - 135 # == Translating a single function call - 136 # This one's easy. Assuming we've already checked things, we just drop the - 137 # outputs (which use hard-coded registers) and emit inputs in a standard format. - 138 # - 139 # out1, out2, out3, ... <- name inout1, inout2, inout3, ... - 140 # => - 141 # (subx-name inout1 inout2 inout3) - 142 # - 143 # Formal types: - 144 # functions: linked list of info - 145 # name: string - 146 # inouts: linked list of vars - 147 # outputs: linked list of vars - 148 # body: block (singleton linked list) - 149 # subx-name: string - 150 - 151 # == Translating a single primitive instruction - 152 # A second crucial piece of the puzzle is how Mu converts fairly regular - 153 # primitives with their uniform syntax to SubX instructions with their gnarly - 154 # x86 details. - 155 # - 156 # Mu instructions have inputs and outputs. Primitives can have up to 2 of - 157 # them. - 158 # SubX instructions have rm32 and r32 operands. - 159 # The translation between them covers almost all the possibilities. - 160 # Instructions with 1 inout may turn into ones with 1 rm32 - 161 # (e.g. incrementing a var on the stack) - 162 # Instructions with 1 output may turn into ones with 1 rm32 - 163 # (e.g. incrementing a var in a register) - 164 # 1 inout and 1 output may turn into 1 rm32 and 1 r32 - 165 # (e.g. adding a var to a reg) - 166 # 2 inouts may turn into 1 rm32 and 1 r32 - 167 # (e.g. adding a reg to a var) - 168 # 1 inout and 1 literal may turn into 1 rm32 and 1 imm32 - 169 # (e.g. adding a constant to a var) - 170 # 1 output and 1 literal may turn into 1 rm32 and 1 imm32 - 171 # (e.g. adding a constant to a reg) - 172 # 2 outputs to hardcoded registers and 1 inout may turn into 1 rm32 - 173 # (special-case: divide edx:eax by a var or reg) - 174 # Observations: - 175 # We always emit rm32. It may be the first inout or the first output. - 176 # We may emit r32 or imm32 or neither. - 177 # When we emit r32 it may come from first inout or second inout or first output. - 178 # - 179 # Accordingly, the formal data structure for a primitive looks like this: - 180 # primitives: linked list of info - 181 # name: string - 182 # mu-inouts: linked list of vars to check - 183 # mu-outputs: linked list of vars to check - 184 # subx-name: string - 185 # subx-rm32: enum arg-location - 186 # subx-r32: enum arg-location - 187 # subx-imm32: enum arg-location - 188 # arg-location: enum - 189 # 0 means none - 190 # 1 means first inout - 191 # 2 means second inout - 192 # 3 means first output - 193 - 194 # == Translating a block - 195 # Emit block name if necessary - 196 # Emit '{' - 197 # When you encounter a statement, emit it as above - 198 # When you encounter a variable declaration - 199 # emit any code needed for it (bzeros) - 200 # push it on the var stack - 201 # update register dict if necessary - 202 # When you encounter '}' - 203 # While popping variables off the var stack until block id changes - 204 # Emit code needed to clean up the stack - 205 # either increment esp - 206 # or pop into appropriate register - 207 - 208 # The rest is straightforward. - 209 - 210 == data - 211 - 212 Program: # (address function) - 213 0/imm32 - 214 - 215 Function-name: - 216 0/imm32 - 217 Function-subx-name: - 218 4/imm32 - 219 Function-inouts: # (address list var) - 220 8/imm32 - 221 Function-outputs: # (address list var) - 222 0xc/imm32 - 223 Function-body: # (address block) - 224 0x10/imm32 - 225 Function-next: # (address function) - 226 0x14/imm32 - 227 Function-size: - 228 0x18/imm32/24 - 229 - 230 Primitive-name: - 231 0/imm32 - 232 Primitive-inouts: # (address list var) - 233 4/imm32 - 234 Primitive-outputs: # (address list var) - 235 8/imm32 - 236 Primitive-subx-name: # (address string) - 237 0xc/imm32 - 238 Primitive-subx-rm32: # enum arg-location - 239 0x10/imm32 - 240 Primitive-subx-r32: # enum arg-location - 241 0x14/imm32 - 242 Primitive-subx-imm32: # enum arg-location - 243 0x18/imm32 - 244 Primitive-next: # (address function) - 245 0x1c/imm32 - 246 Primitive-size: - 247 0x20/imm32/24 - 248 - 249 Stmt-operation: - 250 0/imm32 - 251 Stmt-inouts: - 252 4/imm32 - 253 Stmt-outputs: - 254 8/imm32 - 255 Stmt-next: - 256 0xc/imm32 - 257 Stmt-size: - 258 0x10/imm32 - 259 - 260 Var-name: - 261 0/imm32 - 262 Var-type: - 263 4/imm32 - 264 Var-block: - 265 8/imm32 - 266 Var-stack-offset: - 267 0xc/imm32 - 268 Var-register: - 269 0x10/imm32 - 270 Var-size: - 271 0x14/imm32 - 272 - 273 Any-register: # "*" - 274 # size - 275 1/imm32 - 276 # data - 277 2a/asterisk - 278 - 279 == code + 94 # + 95 # A statement can be: + 96 # tag 0: a block + 97 # tag 1: a simple statement + 98 # tag 2: a variable defined on the stack + 99 # tag 3: a variable defined in a register + 100 # tag 4: a named block + 101 # + 102 # A block contains: + 103 # tag: 0 + 104 # statements: (address list statement) + 105 # + 106 # A regular statement contains: + 107 # tag: 1 + 108 # operation: string + 109 # inouts: (address list operand) + 110 # outputs: (address list var) + 111 # + 112 # A variable defined on the stack contains: + 113 # tag: 2 + 114 # name: string + 115 # type: type-tree + 116 # + 117 # A variable defined in a register contains: + 118 # tag: 3 + 119 # name: string + 120 # type: type-tree + 121 # reg: string + 122 # + 123 # A named block contains: + 124 # tag: 4 + 125 # name: string + 126 # statements: (address list statement) + 127 + 128 # == Translation: managing the stack + 129 # Now that we know what the language looks like in the large, let's think + 130 # about how translation happens from the bottom up. One crucial piece of the + 131 # puzzle is how Mu will clean up variables defined on the stack for you. + 132 # + 133 # Assume that we maintain a 'functions' list while parsing source code. And a + 134 # 'primitives' list is a global constant. Both these contain enough information + 135 # to perform type-checking on function calls or primitive statements, respectively. + 136 # + 137 # Defining variables pushes them on a stack with the current block depth and + 138 # enough information about their location (stack offset or register). + 139 # Starting a block increments the current block id. + 140 # Each statement now has enough information to emit code for it. + 141 # Ending a block is where the magic happens: + 142 # pop all variables at the current block depth + 143 # emit code to restore all register variables introduced at the current depth + 144 # emit code to clean up all stack variables at the current depth (just increment esp) + 145 # decrement the current block depth + 146 # + 147 # Formal types: + 148 # live-vars: stack of vars + 149 # var: + 150 # name: string + 151 # type: s-expression? Just a type id for now. + 152 # block: int + 153 # stack-offset: int (added to ebp) + 154 # register: string + 155 # either usual register names + 156 # or '*' to indicate any register + 157 # At most one of stack-offset or register-index must be non-zero. + 158 # A register of '*' designates a variable _template_. Only legal in formal + 159 # parameters for primitives. + 160 + 161 # == Translating a single function call + 162 # This one's easy. Assuming we've already checked things, we just drop the + 163 # outputs (which use hard-coded registers) and emit inputs in a standard format. + 164 # + 165 # out1, out2, out3, ... <- name inout1, inout2, inout3, ... + 166 # => + 167 # (subx-name inout1 inout2 inout3) + 168 # + 169 # Formal types: + 170 # functions: linked list of info + 171 # name: string + 172 # inouts: linked list of vars + 173 # outputs: linked list of vars + 174 # body: block (singleton linked list) + 175 # subx-name: string + 176 + 177 # == Translating a single primitive instruction + 178 # A second crucial piece of the puzzle is how Mu converts fairly regular + 179 # primitives with their uniform syntax to SubX instructions with their gnarly + 180 # x86 details. + 181 # + 182 # Mu instructions have inputs and outputs. Primitives can have up to 2 of + 183 # them. + 184 # SubX instructions have rm32 and r32 operands. + 185 # The translation between them covers almost all the possibilities. + 186 # Instructions with 1 inout may turn into ones with 1 rm32 + 187 # (e.g. incrementing a var on the stack) + 188 # Instructions with 1 output may turn into ones with 1 rm32 + 189 # (e.g. incrementing a var in a register) + 190 # 1 inout and 1 output may turn into 1 rm32 and 1 r32 + 191 # (e.g. adding a var to a reg) + 192 # 2 inouts may turn into 1 rm32 and 1 r32 + 193 # (e.g. adding a reg to a var) + 194 # 1 inout and 1 literal may turn into 1 rm32 and 1 imm32 + 195 # (e.g. adding a constant to a var) + 196 # 1 output and 1 literal may turn into 1 rm32 and 1 imm32 + 197 # (e.g. adding a constant to a reg) + 198 # 2 outputs to hardcoded registers and 1 inout may turn into 1 rm32 + 199 # (special-case: divide edx:eax by a var or reg) + 200 # Observations: + 201 # We always emit rm32. It may be the first inout or the first output. + 202 # We may emit r32 or imm32 or neither. + 203 # When we emit r32 it may come from first inout or second inout or first output. + 204 # + 205 # Accordingly, the formal data structure for a primitive looks like this: + 206 # primitives: linked list of info + 207 # name: string + 208 # mu-inouts: linked list of vars to check + 209 # mu-outputs: linked list of vars to check + 210 # subx-name: string + 211 # subx-rm32: enum arg-location + 212 # subx-r32: enum arg-location + 213 # subx-imm32: enum arg-location + 214 # arg-location: enum + 215 # 0 means none + 216 # 1 means first inout + 217 # 2 means second inout + 218 # 3 means first output + 219 + 220 # == Translating a block + 221 # Emit block name if necessary + 222 # Emit '{' + 223 # When you encounter a statement, emit it as above + 224 # When you encounter a variable declaration + 225 # emit any code needed for it (bzeros) + 226 # push it on the var stack + 227 # update register dict if necessary + 228 # When you encounter '}' + 229 # While popping variables off the var stack until block id changes + 230 # Emit code needed to clean up the stack + 231 # either increment esp + 232 # or pop into appropriate register + 233 + 234 # The rest is straightforward. + 235 + 236 == data + 237 + 238 Program: # (address function) + 239 0/imm32 + 240 + 241 Function-name: + 242 0/imm32 + 243 Function-subx-name: + 244 4/imm32 + 245 Function-inouts: # (address list var) + 246 8/imm32 + 247 Function-outputs: # (address list var) + 248 0xc/imm32 + 249 Function-body: # (address block) + 250 0x10/imm32 + 251 Function-next: # (address function) + 252 0x14/imm32 + 253 Function-size: + 254 0x18/imm32/24 + 255 + 256 Primitive-name: + 257 0/imm32 + 258 Primitive-inouts: # (address list var) + 259 4/imm32 + 260 Primitive-outputs: # (address list var) + 261 8/imm32 + 262 Primitive-subx-name: # (address string) + 263 0xc/imm32 + 264 Primitive-subx-rm32: # enum arg-location + 265 0x10/imm32 + 266 Primitive-subx-r32: # enum arg-location + 267 0x14/imm32 + 268 Primitive-subx-imm32: # enum arg-location + 269 0x18/imm32 + 270 Primitive-next: # (address function) + 271 0x1c/imm32 + 272 Primitive-size: + 273 0x20/imm32/24 + 274 + 275 Stmt-tag: + 276 0/imm32 + 277 + 278 Block-statements: # (address list statement) + 279 4/imm32 280 - 281 Entry: - 282 # . prologue - 283 89/<- %ebp 4/r32/esp - 284 (new-segment Heap-size Heap) - 285 # if (argv[1] == "test') run-tests() - 286 { - 287 # if (argc <= 1) break - 288 81 7/subop/compare *ebp 1/imm32 - 289 7e/jump-if-lesser-or-equal break/disp8 - 290 # if (argv[1] != "test") break - 291 (kernel-string-equal? *(ebp+8) "test") # => eax - 292 3d/compare-eax-and 0/imm32 - 293 74/jump-if-equal break/disp8 - 294 # - 295 (run-tests) - 296 # syscall(exit, *Num-test-failures) - 297 8b/-> *Num-test-failures 3/r32/ebx - 298 eb/jump $mu-main:end/disp8 - 299 } - 300 # otherwise convert Stdin - 301 (convert-mu Stdin Stdout) - 302 (flush Stdout) - 303 # syscall(exit, 0) - 304 bb/copy-to-ebx 0/imm32 - 305 $mu-main:end: - 306 b8/copy-to-eax 1/imm32/exit - 307 cd/syscall 0x80/imm8 - 308 - 309 convert-mu: # in : (address buffered-file), out : (address buffered-file) - 310 # . prologue - 311 55/push-ebp - 312 89/<- %ebp 4/r32/esp - 313 # - 314 (parse-mu *(ebp+8)) - 315 (check-mu-types) - 316 (emit-subx *(ebp+0xc)) - 317 $convert-mu:end: - 318 # . epilogue - 319 89/<- %esp 5/r32/ebp - 320 5d/pop-to-ebp - 321 c3/return - 322 - 323 test-convert-empty-input: - 324 # empty input => empty output - 325 # . prologue - 326 55/push-ebp - 327 89/<- %ebp 4/r32/esp - 328 # setup - 329 (clear-stream _test-input-stream) - 330 (clear-stream _test-input-buffered-file->buffer) - 331 (clear-stream _test-output-stream) - 332 (clear-stream _test-output-buffered-file->buffer) - 333 # - 334 (convert-mu _test-input-buffered-file _test-output-buffered-file) - 335 (flush _test-output-buffered-file) - 336 (check-stream-equal _test-output-stream "" "F - test-convert-empty-input") - 337 # . epilogue - 338 89/<- %esp 5/r32/ebp - 339 5d/pop-to-ebp - 340 c3/return - 341 - 342 test-convert-function-skeleton: - 343 # empty function decl => function prologue and epilogue - 344 # fn foo { - 345 # } - 346 # => - 347 # foo: - 348 # # . prologue - 349 # 55/push-ebp - 350 # 89/<- %ebp 4/r32/esp - 351 # # . epilogue - 352 # 89/<- %esp 5/r32/ebp - 353 # 5d/pop-to-ebp - 354 # c3/return - 355 # . prologue - 356 55/push-ebp - 357 89/<- %ebp 4/r32/esp - 358 # setup - 359 (clear-stream _test-input-stream) - 360 (clear-stream _test-input-buffered-file->buffer) - 361 (clear-stream _test-output-stream) - 362 (clear-stream _test-output-buffered-file->buffer) - 363 # - 364 (write _test-input-stream "fn foo {\n") - 365 (write _test-input-stream "}\n") - 366 # convert - 367 (convert-mu _test-input-buffered-file _test-output-buffered-file) - 368 (flush _test-output-buffered-file) - 369 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- - 375 # check output - 376 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0") - 377 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-function-skeleton/1") - 378 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-function-skeleton/2") - 379 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3") - 380 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-function-skeleton/4") - 381 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5") - 382 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-function-skeleton/6") - 383 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-function-skeleton/7") - 384 # . epilogue - 385 89/<- %esp 5/r32/ebp - 386 5d/pop-to-ebp - 387 c3/return - 388 - 389 test-convert-multiple-function-skeletons: - 390 # multiple functions correctly organized into a linked list - 391 # fn foo { - 392 # } - 393 # fn bar { - 394 # } - 395 # => - 396 # foo: - 397 # # . prologue - 398 # 55/push-ebp - 399 # 89/<- %ebp 4/r32/esp - 400 # # . epilogue - 401 # 89/<- %esp 5/r32/ebp - 402 # 5d/pop-to-ebp - 403 # c3/return - 404 # bar: - 405 # # . prologue - 406 # 55/push-ebp - 407 # 89/<- %ebp 4/r32/esp - 408 # # . epilogue - 409 # 89/<- %esp 5/r32/ebp - 410 # 5d/pop-to-ebp - 411 # c3/return - 412 # . prologue - 413 55/push-ebp - 414 89/<- %ebp 4/r32/esp - 415 # setup - 416 (clear-stream _test-input-stream) - 417 (clear-stream _test-input-buffered-file->buffer) - 418 (clear-stream _test-output-stream) - 419 (clear-stream _test-output-buffered-file->buffer) - 420 # - 421 (write _test-input-stream "fn foo {\n") - 422 (write _test-input-stream "}\n") - 423 (write _test-input-stream "fn bar {\n") - 424 (write _test-input-stream "}\n") - 425 # convert - 426 (convert-mu _test-input-buffered-file _test-output-buffered-file) - 427 (flush _test-output-buffered-file) - 428 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- - 434 # check first function - 435 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-multiple-function-skeletons/0") - 436 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-multiple-function-skeletons/1") - 437 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-multiple-function-skeletons/2") - 438 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/3") - 439 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-multiple-function-skeletons/4") - 440 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/5") - 441 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/6") - 442 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-multiple-function-skeletons/7") - 443 # check second function - 444 (check-next-stream-line-equal _test-output-stream "bar:" "F - test-convert-multiple-function-skeletons/10") - 445 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-multiple-function-skeletons/11") - 446 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-multiple-function-skeletons/12") - 447 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/13") - 448 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-multiple-function-skeletons/14") - 449 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/15") - 450 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/16") - 451 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-multiple-function-skeletons/17") - 452 # . epilogue - 453 89/<- %esp 5/r32/ebp - 454 5d/pop-to-ebp - 455 c3/return - 456 - 457 test-convert-function-with-arg: - 458 # function with one arg and a copy instruction - 459 # fn foo n : int -> result/eax : int { - 460 # result <- copy n - 461 # } - 462 # => - 463 # foo: - 464 # # . prologue - 465 # 55/push-ebp - 466 # 89/<- %ebp 4/r32/esp - 467 # { - 468 # # result <- copy n - 469 # 8b/-> *(ebp+8) 0/r32/eax - 470 # } - 471 # # . epilogue - 472 # 89/<- %esp 5/r32/ebp - 473 # 5d/pop-to-ebp - 474 # c3/return - 475 # . prologue - 476 55/push-ebp - 477 89/<- %ebp 4/r32/esp - 478 # setup - 479 (clear-stream _test-input-stream) - 480 (clear-stream _test-input-buffered-file->buffer) - 481 (clear-stream _test-output-stream) - 482 (clear-stream _test-output-buffered-file->buffer) - 483 # - 484 (write _test-input-stream "fn foo {\n") - 485 (write _test-input-stream "}\n") - 486 # convert - 487 (convert-mu _test-input-buffered-file _test-output-buffered-file) - 488 (flush _test-output-buffered-file) - 489 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- - 495 # check output - 496 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0") - 497 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-function-skeleton/1") - 498 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-function-skeleton/2") - 499 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3") - 500 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-function-skeleton/4") - 501 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5") - 502 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-function-skeleton/6") - 503 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-function-skeleton/7") - 504 # . epilogue - 505 89/<- %esp 5/r32/ebp - 506 5d/pop-to-ebp - 507 c3/return - 508 - 509 parse-mu: # in : (address buffered-file) - 510 # pseudocode - 511 # var curr-function = Program - 512 # var line : (stream byte 512) - 513 # var word-slice : slice - 514 # while true # line loop - 515 # clear-stream(line) - 516 # read-line-buffered(in, line) - 517 # if (line->write == 0) break # end of file - 518 # while true # word loop - 519 # word-slice = next-word-or-string(line) - 520 # if slice-empty?(word-slice) # end of line - 521 # break - 522 # else if slice-starts-with?(word-slice, "#") # comment - 523 # break # end of line - 524 # else if slice-equal(word-slice, "fn") - 525 # var new-function : (address function) = new function - 526 # populate-mu-function(in, new-function) - 527 # *curr-function = new-function - 528 # curr-function = &new-function->next - 529 # else - 530 # abort() - 531 # - 532 # . prologue - 533 55/push-ebp - 534 89/<- %ebp 4/r32/esp - 535 # . save registers - 536 50/push-eax - 537 51/push-ecx - 538 52/push-edx - 539 57/push-edi - 540 # var line/ecx : (stream byte 512) - 541 81 5/subop/subtract %esp 0x200/imm32 - 542 68/push 0x200/imm32/length - 543 68/push 0/imm32/read - 544 68/push 0/imm32/write - 545 89/<- %ecx 4/r32/esp - 546 # var word-slice/edx : slice - 547 68/push 0/imm32/end - 548 68/push 0/imm32/start - 549 89/<- %edx 4/r32/esp - 550 # var curr-function/edi : (address function) = Program - 551 bf/copy-to-edi Program/imm32 - 552 { - 553 $parse-mu:line-loop: - 554 (clear-stream %ecx) - 555 (read-line-buffered *(ebp+8) %ecx) - 556 # if (line->write == 0) break - 557 81 7/subop/compare *ecx 0/imm32 - 558 0f 84/jump-if-equal break/disp32 - 559 +-- 6 lines: #? # dump line --------------------------------------------------------------------------------------------------------------------------- - 565 { # word loop - 566 $parse-mu:word-loop: - 567 (next-word-or-string %ecx %edx) - 568 # if slice-empty?(word-slice) break - 569 (slice-empty? %edx) - 570 3d/compare-eax-and 0/imm32 - 571 0f 85/jump-if-not-equal break/disp32 - 572 # if (*word-slice->start == "#") break - 573 # . eax = *word-slice->start - 574 8b/-> *edx 0/r32/eax - 575 8a/copy-byte *eax 0/r32/AL - 576 81 4/subop/and %eax 0xff/imm32 - 577 # . if (eax == '#') break - 578 3d/compare-eax-and 0x23/imm32/hash - 579 0f 84/jump-if-equal break/disp32 - 580 # if (slice-equal?(word-slice, "fn")) parse a function - 581 { - 582 (slice-equal? %edx "fn") - 583 3d/compare-eax-and 0/imm32 - 584 0f 84/jump-if-equal break/disp32 - 585 # var new-function/eax : (address function) = populate-mu-function() - 586 (allocate Heap *Function-size) # => eax - 587 (populate-mu-function-header %ecx %eax) - 588 (populate-mu-function-body *(ebp+8) %eax) - 589 # *curr-function = new-function - 590 89/<- *edi 0/r32/eax - 591 # curr-function = &new-function->next - 592 8d/address-> *(eax+0x10) 7/r32/edi - 593 e9/jump $parse-mu:word-loop/disp32 - 594 } - 595 # otherwise abort - 596 e9/jump $parse-mu:abort/disp32 - 597 } # end word loop - 598 e9/jump loop/disp32 - 599 } # end line loop - 600 $parse-mu:end: - 601 # . reclaim locals - 602 81 0/subop/add %esp 0x214/imm32 - 603 # . restore registers - 604 5f/pop-to-edi - 605 5a/pop-to-edx - 606 59/pop-to-ecx - 607 58/pop-to-eax - 608 # . epilogue - 609 89/<- %esp 5/r32/ebp - 610 5d/pop-to-ebp - 611 c3/return - 612 - 613 $parse-mu:abort: - 614 # error("unexpected top-level command: " word-slice "\n") - 615 (write-buffered Stderr "unexpected top-level command: ") - 616 (write-buffered Stderr %edx) - 617 (write-buffered Stderr "\n") - 618 (flush Stderr) - 619 # . syscall(exit, 1) - 620 bb/copy-to-ebx 1/imm32 - 621 b8/copy-to-eax 1/imm32/exit - 622 cd/syscall 0x80/imm8 - 623 # never gets here - 624 - 625 # errors considered: - 626 # fn foo { { - 627 # fn foo { } - 628 # fn foo { } { - 629 # fn foo # no block - 630 populate-mu-function-header: # first-line : (address stream byte), out : (address function) - 631 # . prologue - 632 55/push-ebp - 633 89/<- %ebp 4/r32/esp - 634 # . save registers - 635 50/push-eax - 636 51/push-ecx - 637 57/push-edi - 638 # edi = out - 639 8b/-> *(ebp+0xc) 7/r32/edi - 640 # var word-slice/ecx : slice - 641 68/push 0/imm32/end - 642 68/push 0/imm32/start - 643 89/<- %ecx 4/r32/esp - 644 # save function name - 645 (next-word *(ebp+8) %ecx) - 646 (slice-to-string Heap %ecx) # => eax - 647 89/<- *edi 0/r32/eax - 648 # assert that next token is '{' - 649 (next-word *(ebp+8) %ecx) - 650 (slice-equal? %ecx "{") - 651 3d/compare-eax-and 0/imm32 - 652 74/jump-if-equal $populate-mu-function-header:abort/disp8 - 653 # assert that there's no further token - 654 { - 655 # word-slice = next-word(line) - 656 (next-word *(ebp+8) %ecx) - 657 # if (word-slice == '') break - 658 (slice-empty? %ecx) - 659 3d/compare-eax-and 0/imm32 - 660 75/jump-if-not-equal break/disp8 - 661 # if (slice-starts-with?(word-slice, "#")) break - 662 # . eax = *word-slice->start - 663 8b/-> *edx 0/r32/eax - 664 8a/copy-byte *eax 0/r32/AL - 665 81 4/subop/and %eax 0xff/imm32 - 666 # . if (eax == '#') break - 667 3d/compare-eax-and 0x23/imm32/hash - 668 74/jump-if-equal break/disp8 - 669 # otherwise abort - 670 eb/jump $populate-mu-function-header:abort/disp8 - 671 } - 672 $populate-mu-function-header:end: - 673 # . reclaim locals - 674 81 0/subop/add %esp 8/imm32 - 675 # . restore registers - 676 5f/pop-to-edi - 677 59/pop-to-ecx - 678 58/pop-to-eax - 679 # . epilogue - 680 89/<- %esp 5/r32/ebp - 681 5d/pop-to-ebp - 682 c3/return - 683 - 684 $populate-mu-function-header:abort: - 685 # error("function header not in form 'fn <name> {'") - 686 (write-buffered Stderr "function header not in form 'fn <name> {' -- '") - 687 (rewind-stream *(ebp+8)) - 688 (write-stream 2 *(ebp+8)) - 689 (write-buffered Stderr "'\n") - 690 (flush Stderr) - 691 # . syscall(exit, 1) - 692 bb/copy-to-ebx 1/imm32 - 693 b8/copy-to-eax 1/imm32/exit - 694 cd/syscall 0x80/imm8 - 695 # never gets here - 696 - 697 # errors considered: - 698 # { abc - 699 populate-mu-function-body: # in : (address buffered-file), out : (address function) - 700 # . prologue - 701 55/push-ebp - 702 89/<- %ebp 4/r32/esp - 703 # . save registers - 704 50/push-eax - 705 51/push-ecx - 706 52/push-edx - 707 53/push-ebx - 708 # var line/ecx : (stream byte 512) - 709 81 5/subop/subtract %esp 0x200/imm32 - 710 68/push 0x200/imm32/length - 711 68/push 0/imm32/read - 712 68/push 0/imm32/write - 713 89/<- %ecx 4/r32/esp - 714 # var word-slice/edx : slice - 715 68/push 0/imm32/end - 716 68/push 0/imm32/start - 717 89/<- %edx 4/r32/esp - 718 # var open-curly-count/ebx : int = 1 - 719 bb/copy-to-ebx 1/imm32 - 720 { # line loop - 721 $populate-mu-function-body:line-loop: - 722 # if (open-curly-count == 0) break - 723 81 7/subop/compare %ebx 0/imm32 - 724 0f 84/jump-if-equal break/disp32 - 725 # line = read-line-buffered(in) - 726 (clear-stream %ecx) - 727 (read-line-buffered *(ebp+8) %ecx) - 728 # if (line->write == 0) break - 729 81 7/subop/compare *ecx 0/imm32 - 730 0f 84/jump-if-equal break/disp32 - 731 # word-slice = next-word(line) - 732 (next-word %ecx %edx) - 733 # if slice-empty?(word-slice) continue - 734 (slice-empty? %ecx) - 735 3d/compare-eax-and 0/imm32 - 736 75/jump-if-not-equal loop/disp8 - 737 # if (slice-starts-with?(word-slice, '#') continue - 738 # . eax = *word-slice->start - 739 8b/-> *edx 0/r32/eax - 740 8a/copy-byte *eax 0/r32/AL - 741 81 4/subop/and %eax 0xff/imm32 - 742 # . if (eax == '#') continue - 743 3d/compare-eax-and 0x23/imm32/hash - 744 74/jump-if-equal loop/disp8 - 745 { - 746 # if slice-equal?(word-slice, "{") ++open-curly-count - 747 { - 748 (slice-equal? %ecx "{") - 749 3d/compare-eax-and 0/imm32 - 750 74/jump-if-equal break/disp8 - 751 43/increment-ebx - 752 eb/jump $curly-found:end/disp8 - 753 } - 754 # else if slice-equal?(word-slice, "}") --open-curly-count - 755 { - 756 (slice-equal? %ecx "}") - 757 3d/compare-eax-and 0/imm32 - 758 74/jump-if-equal break/disp8 - 759 4b/decrement-ebx - 760 eb/jump $curly-found:end/disp8 - 761 } - 762 # else break - 763 eb/jump $populate-mu-function-body:end/disp8 - 764 } - 765 # - check for invalid tokens after curly - 766 $curly-found:end: - 767 # second-word-slice = next-word(line) - 768 (next-word %ecx %edx) - 769 # if slice-empty?(second-word-slice) continue - 770 (slice-empty? %ecx) - 771 3d/compare-eax-and 0/imm32 - 772 0f 85/jump-if-not-equal loop/disp32 - 773 # if (slice-starts-with?(second-word-slice, '#') continue - 774 # . eax = *second-word-slice->start - 775 8b/-> *edx 0/r32/eax - 776 8a/copy-byte *eax 0/r32/AL - 777 81 4/subop/and %eax 0xff/imm32 - 778 # . if (eax == '#') continue - 779 3d/compare-eax-and 0x23/imm32/hash - 780 0f 84/jump-if-equal loop/disp32 - 781 # abort - 782 eb/jump $populate-mu-function-body:abort/disp8 - 783 } # end line loop - 784 $populate-mu-function-body:end: - 785 # . reclaim locals - 786 81 0/subop/add %esp 0x214/imm32 - 787 # . restore registers - 788 5b/pop-to-ebx - 789 5a/pop-to-edx - 790 59/pop-to-ecx - 791 58/pop-to-eax - 792 # . epilogue - 793 89/<- %esp 5/r32/ebp - 794 5d/pop-to-ebp - 795 c3/return - 796 - 797 $populate-mu-function-body:abort: - 798 # error("'{' or '}' should be on its own line, but got '") - 799 (write-buffered Stderr "'{' or '}' should be on its own line, but got '") - 800 (rewind-stream %ecx) - 801 (write-stream 2 %ecx) - 802 (write-buffered Stderr "'\n") - 803 (flush Stderr) - 804 # . syscall(exit, 1) - 805 bb/copy-to-ebx 1/imm32 - 806 b8/copy-to-eax 1/imm32/exit - 807 cd/syscall 0x80/imm8 - 808 # never gets here - 809 - 810 check-mu-types: - 811 # . prologue - 812 55/push-ebp - 813 89/<- %ebp 4/r32/esp - 814 # - 815 $check-types:end: - 816 # . epilogue - 817 89/<- %esp 5/r32/ebp - 818 5d/pop-to-ebp - 819 c3/return - 820 - 821 emit-subx: # out : (address buffered-file) - 822 # . prologue - 823 55/push-ebp - 824 89/<- %ebp 4/r32/esp - 825 # . save registers - 826 50/push-eax - 827 51/push-ecx - 828 57/push-edi - 829 # edi = out - 830 8b/-> *(ebp+8) 7/r32/edi - 831 # var curr/ecx : (address function) = Program - 832 8b/-> *Program 1/r32/ecx - 833 { - 834 # if (curr == NULL) break - 835 81 7/subop/compare %ecx 0/imm32 - 836 0f 84/jump-if-equal break/disp32 - 837 (emit-subx-function %edi %ecx) - 838 # curr = curr->next - 839 8b/-> *(ecx+0x10) 1/r32/ecx - 840 e9/jump loop/disp32 - 841 } - 842 $emit-subx:end: - 843 # . restore registers - 844 5f/pop-to-edi - 845 59/pop-to-ecx - 846 58/pop-to-eax - 847 # . epilogue - 848 89/<- %esp 5/r32/ebp - 849 5d/pop-to-ebp - 850 c3/return - 851 - 852 # == Emitting a function - 853 # Emit function header - 854 # Emit function prologue - 855 # Translate function body - 856 # Emit function epilogue - 857 - 858 emit-subx-function: # out : (address buffered-file), f : (address function) - 859 # . prologue - 860 55/push-ebp - 861 89/<- %ebp 4/r32/esp - 862 # . save registers - 863 50/push-eax - 864 51/push-ecx - 865 57/push-edi - 866 # edi = out - 867 8b/-> *(ebp+8) 7/r32/edi - 868 # ecx = f - 869 8b/-> *(ebp+0xc) 1/r32/ecx - 870 # - 871 (write-buffered %edi *ecx) - 872 (write-buffered %edi ":\n") - 873 (emit-subx-prologue %edi) - 874 (emit-subx-block %edi *(ecx+0x10)) # Function-body - 875 (emit-subx-epilogue %edi) - 876 $emit-subx-function:end: - 877 # . restore registers - 878 5f/pop-to-edi - 879 59/pop-to-ecx - 880 58/pop-to-eax - 881 # . epilogue - 882 89/<- %esp 5/r32/ebp - 883 5d/pop-to-ebp - 884 c3/return - 885 - 886 emit-subx-block: # out : (address buffered-file), block : (address block) - 887 # . prologue - 888 55/push-ebp - 889 89/<- %ebp 4/r32/esp - 890 # - 891 $emit-subx-block:end: - 892 # . epilogue - 893 89/<- %esp 5/r32/ebp - 894 5d/pop-to-ebp - 895 c3/return - 896 - 897 emit-subx-statement: # out : (address buffered-file), stmt : (address statement), vars : (stack var), primitives : (address primitive), functions : (address function) - 898 # . prologue - 899 55/push-ebp - 900 89/<- %ebp 4/r32/esp - 901 # . save registers - 902 50/push-eax - 903 51/push-ecx - 904 # if stmt matches a primitive, emit it - 905 { - 906 $emit-subx-statement:primitive: - 907 (find-matching-primitive *(ebp+0x14) *(ebp+0xc)) # primitives, stmt => curr/eax - 908 3d/compare-eax-and 0/imm32 - 909 74/jump-if-equal break/disp8 - 910 (emit-subx-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) # out, stmt, vars, curr - 911 e9/jump $emit-subx-statement:end/disp32 - 912 } - 913 # else if stmt matches a function, emit a call to it - 914 { - 915 $emit-subx-statement:call: - 916 (find-matching-function *(ebp+0x18) *(ebp+0xc)) # functions, stmt => curr/eax - 917 3d/compare-eax-and 0/imm32 - 918 74/jump-if-equal break/disp8 - 919 (emit-subx-call *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) # out, stmt, vars, curr - 920 e9/jump $emit-subx-statement:end/disp32 - 921 } - 922 # else abort - 923 e9/jump $emit-subx-statement:abort/disp32 - 924 $emit-subx-statement:end: - 925 # . restore registers - 926 59/pop-to-ecx - 927 58/pop-to-eax - 928 # . epilogue - 929 89/<- %esp 5/r32/ebp - 930 5d/pop-to-ebp - 931 c3/return - 932 - 933 $emit-subx-statement:abort: - 934 # error("couldn't translate '" stmt "'\n") - 935 (write-buffered Stderr "couldn't translate '") - 936 #? (emit-string Stderr *(ebp+0xc)) # TODO - 937 (write-buffered Stderr "'\n") - 938 (flush Stderr) - 939 # . syscall(exit, 1) - 940 bb/copy-to-ebx 1/imm32 - 941 b8/copy-to-eax 1/imm32/exit - 942 cd/syscall 0x80/imm8 - 943 # never gets here - 944 - 945 # Primitives supported - 946 == data - 947 Primitives: - 948 # increment var => ff 0/subop/increment *(ebp+__) - 949 "increment"/imm32/name - 950 Single-int-var-on-stack/imm32/inouts - 951 0/imm32/no-outputs - 952 "ff 0/subop/increment"/imm32/subx-name - 953 1/imm32/rm32-is-first-inout - 954 0/imm32/no-r32 - 955 0/imm32/no-imm32 - 956 _Primitive-inc-reg/imm32/next - 957 _Primitive-inc-reg: - 958 # var/reg <- increment => ff 0/subop/increment %__ - 959 "increment"/imm32/name - 960 0/imm32/no-inouts - 961 Single-int-var-in-some-register/imm32/outputs - 962 "ff 0/subop/increment"/imm32/subx-name - 963 3/imm32/rm32-is-first-output - 964 0/imm32/no-r32 - 965 0/imm32/no-imm32 - 966 _Primitive-add-reg-to-reg/imm32/next - 967 _Primitive-add-reg-to-reg: - 968 # var1/reg <- add var2/reg => 01 var1/rm32 var2/r32 - 969 "add"/imm32/name - 970 Single-int-var-in-some-register/imm32/inouts - 971 Single-int-var-in-some-register/imm32/outputs - 972 "01"/imm32/subx-name - 973 3/imm32/rm32-is-first-output - 974 1/imm32/r32-is-first-inout - 975 0/imm32/no-imm32 - 976 _Primitive-add-lit-to-reg/imm32/next - 977 _Primitive-add-lit-to-reg: - 978 # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32 - 979 "add"/imm32/name - 980 Single-lit-var/imm32/inouts - 981 Single-int-var-in-some-register/imm32/outputs - 982 "81 0/subop/add"/imm32/subx-name - 983 3/imm32/rm32-is-first-output - 984 0/imm32/no-r32 - 985 1/imm32/imm32-is-first-inout - 986 0/imm32/next - 987 - 988 Single-int-var-on-stack: - 989 Int-var-on-stack/imm32 - 990 0/imm32/next - 991 - 992 Int-var-on-stack: - 993 "arg1"/imm32/name - 994 1/imm32/type-int - 995 1/imm32/some-block-depth - 996 1/imm32/some-stack-offset - 997 0/imm32/no-register - 998 - 999 Single-int-var-in-some-register: -1000 Int-var-in-some-register/imm32 -1001 0/imm32/next -1002 -1003 Int-var-in-some-register: -1004 "arg1"/imm32/name -1005 1/imm32/type-int -1006 1/imm32/some-block-depth -1007 0/imm32/some-stack-offset -1008 "*"/imm32/register -1009 -1010 Single-lit-var: -1011 Lit-var/imm32 -1012 0/imm32/next -1013 -1014 Lit-var: -1015 "literal"/imm32/name -1016 0/imm32/type-literal -1017 1/imm32/some-block-depth -1018 0/imm32/no-stack-offset -1019 0/imm32/no-register -1020 -1021 == code -1022 emit-subx-primitive: # out : (address buffered-file), stmt : (address statement), vars : (address variable), primitive : (address function) -1023 # . prologue -1024 55/push-ebp -1025 89/<- %ebp 4/r32/esp -1026 # . save registers -1027 50/push-eax -1028 51/push-ecx -1029 # ecx = primitive -1030 8b/-> *(ebp+0x14) 1/r32/ecx -1031 # emit primitive name -1032 (write-buffered *(ebp+8) *(ecx+0xc)) # Primitive-subx-name -1033 # emit rm32 if necessary -1034 (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc)) # out, Primitive-subx-rm32, stmt -1035 # emit r32 if necessary -1036 (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc)) # out, Primitive-subx-r32, stmt -1037 # emit imm32 if necessary -1038 (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc)) # out, Primitive-subx-imm32, stmt -1039 $emit-subx-primitive:end: -1040 # . restore registers -1041 59/pop-to-ecx -1042 58/pop-to-eax -1043 # . epilogue -1044 89/<- %esp 5/r32/ebp -1045 5d/pop-to-ebp -1046 c3/return -1047 -1048 emit-subx-rm32: # out : (address buffered-file), l : arg-location, stmt : (address statement) -1049 # . prologue -1050 55/push-ebp -1051 89/<- %ebp 4/r32/esp -1052 # . save registers -1053 50/push-eax -1054 # if (l == 0) return -1055 81 7/subop/compare *(ebp+0xc) 0/imm32 -1056 74/jump-if-equal $emit-subx-rm32:end/disp8 -1057 # -1058 (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # stmt, l => var/eax -1059 (emit-subx-var-as-rm32 *(ebp+8) %eax) # out, var -1060 $emit-subx-rm32:end: -1061 # . restore registers -1062 58/pop-to-eax -1063 # . epilogue -1064 89/<- %esp 5/r32/ebp -1065 5d/pop-to-ebp -1066 c3/return -1067 -1068 get-stmt-operand-from-arg-location: # stmt : (address statement), l : arg-location -> var/eax : (address variable) -1069 # . prologue -1070 55/push-ebp -1071 89/<- %ebp 4/r32/esp -1072 # . save registers -1073 51/push-ecx -1074 # eax = l -1075 8b/-> *(ebp+0xc) 0/r32/eax -1076 # ecx = stmt -1077 8b/-> *(ebp+8) 1/r32/ecx -1078 # if (l == 1) return stmt->inouts->var -1079 { -1080 3d/compare-eax-and 1/imm32 -1081 75/jump-if-not-equal break/disp8 -1082 $get-stmt-operand-from-arg-location:1: -1083 8b/-> *(ecx+4) 0/r32/eax # Stmt-inouts -1084 8b/-> *eax 0/r32/eax # Operand-var -1085 eb/jump $get-stmt-operand-from-arg-location:end/disp8 -1086 } -1087 # if (l == 2) return stmt->inouts->next->var -1088 { -1089 3d/compare-eax-and 2/imm32 -1090 75/jump-if-not-equal break/disp8 -1091 $get-stmt-operand-from-arg-location:2: -1092 8b/-> *(ecx+4) 0/r32/eax # Stmt-inouts -1093 8b/-> *(eax+4) 0/r32/eax # Operand-next -1094 8b/-> *eax 0/r32/eax # Operand-var -1095 eb/jump $get-stmt-operand-from-arg-location:end/disp8 -1096 } -1097 # if (l == 3) return stmt->outputs -1098 { -1099 3d/compare-eax-and 3/imm32 -1100 75/jump-if-not-equal break/disp8 -1101 $get-stmt-operand-from-arg-location:3: -1102 8b/-> *(ecx+8) 0/r32/eax # Stmt-outputs -1103 8b/-> *eax 0/r32/eax # Operand-var -1104 eb/jump $get-stmt-operand-from-arg-location:end/disp8 -1105 } -1106 # abort -1107 e9/jump $get-stmt-operand-from-arg-location:abort/disp32 -1108 $get-stmt-operand-from-arg-location:end: -1109 # . restore registers -1110 59/pop-to-ecx -1111 # . epilogue -1112 89/<- %esp 5/r32/ebp -1113 5d/pop-to-ebp -1114 c3/return + 281 Stmt1-operation: # string + 282 4/imm32 + 283 Stmt1-inouts: # (address list operand) + 284 8/imm32 + 285 Stmt1-outputs: # (address list var) + 286 0xc/imm32 + 287 + 288 Vardef-name: # string + 289 4/imm32 + 290 Vardef-type: # (address tree type-id) + 291 8/imm32 + 292 + 293 Regvardef-name: # string + 294 4/imm32 + 295 Regvardef-type: # (address tree type-id) + 296 8/imm32 + 297 Regvardef-register: # string + 298 0xc/imm32 + 299 + 300 Named-block-name: + 301 4/imm32 + 302 Named-block-statements: # (address list statement) + 303 8/imm32 + 304 + 305 Stmt-operation: + 306 0/imm32 + 307 Stmt-inouts: + 308 4/imm32 + 309 Stmt-outputs: + 310 8/imm32 + 311 Stmt-next: + 312 0xc/imm32 + 313 Stmt-size: + 314 0x10/imm32 + 315 + 316 Var-name: + 317 0/imm32 + 318 Var-type: + 319 4/imm32 + 320 Var-block: + 321 8/imm32 + 322 Var-stack-offset: + 323 0xc/imm32 + 324 Var-register: + 325 0x10/imm32 + 326 Var-size: + 327 0x14/imm32 + 328 + 329 Any-register: # "*" + 330 # size + 331 1/imm32 + 332 # data + 333 2a/asterisk + 334 + 335 == code + 336 + 337 Entry: + 338 # . prologue + 339 89/<- %ebp 4/r32/esp + 340 (new-segment Heap-size Heap) + 341 # if (argv[1] == "test') run-tests() + 342 { + 343 # if (argc <= 1) break + 344 81 7/subop/compare *ebp 1/imm32 + 345 7e/jump-if-lesser-or-equal break/disp8 + 346 # if (argv[1] != "test") break + 347 (kernel-string-equal? *(ebp+8) "test") # => eax + 348 3d/compare-eax-and 0/imm32 + 349 74/jump-if-equal break/disp8 + 350 # + 351 (run-tests) + 352 # syscall(exit, *Num-test-failures) + 353 8b/-> *Num-test-failures 3/r32/ebx + 354 eb/jump $mu-main:end/disp8 + 355 } + 356 # otherwise convert Stdin + 357 (convert-mu Stdin Stdout) + 358 (flush Stdout) + 359 # syscall(exit, 0) + 360 bb/copy-to-ebx 0/imm32 + 361 $mu-main:end: + 362 b8/copy-to-eax 1/imm32/exit + 363 cd/syscall 0x80/imm8 + 364 + 365 convert-mu: # in : (address buffered-file), out : (address buffered-file) + 366 # . prologue + 367 55/push-ebp + 368 89/<- %ebp 4/r32/esp + 369 # + 370 (parse-mu *(ebp+8)) + 371 (check-mu-types) + 372 (emit-subx *(ebp+0xc)) + 373 $convert-mu:end: + 374 # . epilogue + 375 89/<- %esp 5/r32/ebp + 376 5d/pop-to-ebp + 377 c3/return + 378 + 379 test-convert-empty-input: + 380 # empty input => empty output + 381 # . prologue + 382 55/push-ebp + 383 89/<- %ebp 4/r32/esp + 384 # setup + 385 (clear-stream _test-input-stream) + 386 (clear-stream _test-input-buffered-file->buffer) + 387 (clear-stream _test-output-stream) + 388 (clear-stream _test-output-buffered-file->buffer) + 389 # + 390 (convert-mu _test-input-buffered-file _test-output-buffered-file) + 391 (flush _test-output-buffered-file) + 392 (check-stream-equal _test-output-stream "" "F - test-convert-empty-input") + 393 # . epilogue + 394 89/<- %esp 5/r32/ebp + 395 5d/pop-to-ebp + 396 c3/return + 397 + 398 test-convert-function-skeleton: + 399 # empty function decl => function prologue and epilogue + 400 # fn foo { + 401 # } + 402 # => + 403 # foo: + 404 # # . prologue + 405 # 55/push-ebp + 406 # 89/<- %ebp 4/r32/esp + 407 # # . epilogue + 408 # 89/<- %esp 5/r32/ebp + 409 # 5d/pop-to-ebp + 410 # c3/return + 411 # . prologue + 412 55/push-ebp + 413 89/<- %ebp 4/r32/esp + 414 # setup + 415 (clear-stream _test-input-stream) + 416 (clear-stream _test-input-buffered-file->buffer) + 417 (clear-stream _test-output-stream) + 418 (clear-stream _test-output-buffered-file->buffer) + 419 # + 420 (write _test-input-stream "fn foo {\n") + 421 (write _test-input-stream "}\n") + 422 # convert + 423 (convert-mu _test-input-buffered-file _test-output-buffered-file) + 424 (flush _test-output-buffered-file) + 425 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 431 # check output + 432 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0") + 433 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-function-skeleton/1") + 434 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-function-skeleton/2") + 435 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3") + 436 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-function-skeleton/4") + 437 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5") + 438 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-function-skeleton/6") + 439 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-function-skeleton/7") + 440 # . epilogue + 441 89/<- %esp 5/r32/ebp + 442 5d/pop-to-ebp + 443 c3/return + 444 + 445 test-convert-multiple-function-skeletons: + 446 # multiple functions correctly organized into a linked list + 447 # fn foo { + 448 # } + 449 # fn bar { + 450 # } + 451 # => + 452 # foo: + 453 # # . prologue + 454 # 55/push-ebp + 455 # 89/<- %ebp 4/r32/esp + 456 # # . epilogue + 457 # 89/<- %esp 5/r32/ebp + 458 # 5d/pop-to-ebp + 459 # c3/return + 460 # bar: + 461 # # . prologue + 462 # 55/push-ebp + 463 # 89/<- %ebp 4/r32/esp + 464 # # . epilogue + 465 # 89/<- %esp 5/r32/ebp + 466 # 5d/pop-to-ebp + 467 # c3/return + 468 # . prologue + 469 55/push-ebp + 470 89/<- %ebp 4/r32/esp + 471 # setup + 472 (clear-stream _test-input-stream) + 473 (clear-stream _test-input-buffered-file->buffer) + 474 (clear-stream _test-output-stream) + 475 (clear-stream _test-output-buffered-file->buffer) + 476 # + 477 (write _test-input-stream "fn foo {\n") + 478 (write _test-input-stream "}\n") + 479 (write _test-input-stream "fn bar {\n") + 480 (write _test-input-stream "}\n") + 481 # convert + 482 (convert-mu _test-input-buffered-file _test-output-buffered-file) + 483 (flush _test-output-buffered-file) + 484 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 490 # check first function + 491 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-multiple-function-skeletons/0") + 492 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-multiple-function-skeletons/1") + 493 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-multiple-function-skeletons/2") + 494 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/3") + 495 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-multiple-function-skeletons/4") + 496 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/5") + 497 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/6") + 498 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-multiple-function-skeletons/7") + 499 # check second function + 500 (check-next-stream-line-equal _test-output-stream "bar:" "F - test-convert-multiple-function-skeletons/10") + 501 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-multiple-function-skeletons/11") + 502 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-multiple-function-skeletons/12") + 503 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-multiple-function-skeletons/13") + 504 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-multiple-function-skeletons/14") + 505 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-multiple-function-skeletons/15") + 506 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-multiple-function-skeletons/16") + 507 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-multiple-function-skeletons/17") + 508 # . epilogue + 509 89/<- %esp 5/r32/ebp + 510 5d/pop-to-ebp + 511 c3/return + 512 + 513 test-convert-function-with-arg: + 514 # function with one arg and a copy instruction + 515 # fn foo n : int -> result/eax : int { + 516 # result <- copy n + 517 # } + 518 # => + 519 # foo: + 520 # # . prologue + 521 # 55/push-ebp + 522 # 89/<- %ebp 4/r32/esp + 523 # { + 524 # # result <- copy n + 525 # 8b/-> *(ebp+8) 0/r32/eax + 526 # } + 527 # # . epilogue + 528 # 89/<- %esp 5/r32/ebp + 529 # 5d/pop-to-ebp + 530 # c3/return + 531 # . prologue + 532 55/push-ebp + 533 89/<- %ebp 4/r32/esp + 534 # setup + 535 (clear-stream _test-input-stream) + 536 (clear-stream _test-input-buffered-file->buffer) + 537 (clear-stream _test-output-stream) + 538 (clear-stream _test-output-buffered-file->buffer) + 539 # + 540 (write _test-input-stream "fn foo {\n") + 541 (write _test-input-stream "}\n") + 542 # convert + 543 (convert-mu _test-input-buffered-file _test-output-buffered-file) + 544 (flush _test-output-buffered-file) + 545 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 551 # check output + 552 (check-next-stream-line-equal _test-output-stream "foo:" "F - test-convert-function-skeleton/0") + 553 (check-next-stream-line-equal _test-output-stream "# . prologue" "F - test-convert-function-skeleton/1") + 554 (check-next-stream-line-equal _test-output-stream "55/push-ebp" "F - test-convert-function-skeleton/2") + 555 (check-next-stream-line-equal _test-output-stream "89/<- %ebp 4/r32/esp" "F - test-convert-function-skeleton/3") + 556 (check-next-stream-line-equal _test-output-stream "# . epilogue" "F - test-convert-function-skeleton/4") + 557 (check-next-stream-line-equal _test-output-stream "89/<- %esp 5/r32/ebp" "F - test-convert-function-skeleton/5") + 558 (check-next-stream-line-equal _test-output-stream "5d/pop-to-ebp" "F - test-convert-function-skeleton/6") + 559 (check-next-stream-line-equal _test-output-stream "c3/return" "F - test-convert-function-skeleton/7") + 560 # . epilogue + 561 89/<- %esp 5/r32/ebp + 562 5d/pop-to-ebp + 563 c3/return + 564 + 565 parse-mu: # in : (address buffered-file) + 566 # pseudocode + 567 # var curr-function = Program + 568 # var line : (stream byte 512) + 569 # var word-slice : slice + 570 # while true # line loop + 571 # clear-stream(line) + 572 # read-line-buffered(in, line) + 573 # if (line->write == 0) break # end of file + 574 # while true # word loop + 575 # word-slice = next-word-or-string(line) + 576 # if slice-empty?(word-slice) # end of line + 577 # break + 578 # else if slice-starts-with?(word-slice, "#") # comment + 579 # break # end of line + 580 # else if slice-equal(word-slice, "fn") + 581 # var new-function : (address function) = new function + 582 # populate-mu-function(in, new-function) + 583 # *curr-function = new-function + 584 # curr-function = &new-function->next + 585 # else + 586 # abort() + 587 # + 588 # . prologue + 589 55/push-ebp + 590 89/<- %ebp 4/r32/esp + 591 # . save registers + 592 50/push-eax + 593 51/push-ecx + 594 52/push-edx + 595 57/push-edi + 596 # var line/ecx : (stream byte 512) + 597 81 5/subop/subtract %esp 0x200/imm32 + 598 68/push 0x200/imm32/length + 599 68/push 0/imm32/read + 600 68/push 0/imm32/write + 601 89/<- %ecx 4/r32/esp + 602 # var word-slice/edx : slice + 603 68/push 0/imm32/end + 604 68/push 0/imm32/start + 605 89/<- %edx 4/r32/esp + 606 # var curr-function/edi : (address function) = Program + 607 bf/copy-to-edi Program/imm32 + 608 { + 609 $parse-mu:line-loop: + 610 (clear-stream %ecx) + 611 (read-line-buffered *(ebp+8) %ecx) + 612 # if (line->write == 0) break + 613 81 7/subop/compare *ecx 0/imm32 + 614 0f 84/jump-if-equal break/disp32 + 615 +-- 6 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 621 { # word loop + 622 $parse-mu:word-loop: + 623 (next-word-or-string %ecx %edx) + 624 # if slice-empty?(word-slice) break + 625 (slice-empty? %edx) + 626 3d/compare-eax-and 0/imm32 + 627 0f 85/jump-if-not-equal break/disp32 + 628 # if (*word-slice->start == "#") break + 629 # . eax = *word-slice->start + 630 8b/-> *edx 0/r32/eax + 631 8a/copy-byte *eax 0/r32/AL + 632 81 4/subop/and %eax 0xff/imm32 + 633 # . if (eax == '#') break + 634 3d/compare-eax-and 0x23/imm32/hash + 635 0f 84/jump-if-equal break/disp32 + 636 # if (slice-equal?(word-slice, "fn")) parse a function + 637 { + 638 (slice-equal? %edx "fn") + 639 3d/compare-eax-and 0/imm32 + 640 0f 84/jump-if-equal break/disp32 + 641 # var new-function/eax : (address function) = populate-mu-function() + 642 (allocate Heap *Function-size) # => eax + 643 (populate-mu-function-header %ecx %eax) + 644 (populate-mu-function-body *(ebp+8) %eax) + 645 # *curr-function = new-function + 646 89/<- *edi 0/r32/eax + 647 # curr-function = &new-function->next + 648 8d/address-> *(eax+0x14) 7/r32/edi # Function-next + 649 e9/jump $parse-mu:word-loop/disp32 + 650 } + 651 # otherwise abort + 652 e9/jump $parse-mu:abort/disp32 + 653 } # end word loop + 654 e9/jump loop/disp32 + 655 } # end line loop + 656 $parse-mu:end: + 657 # . reclaim locals + 658 81 0/subop/add %esp 0x214/imm32 + 659 # . restore registers + 660 5f/pop-to-edi + 661 5a/pop-to-edx + 662 59/pop-to-ecx + 663 58/pop-to-eax + 664 # . epilogue + 665 89/<- %esp 5/r32/ebp + 666 5d/pop-to-ebp + 667 c3/return + 668 + 669 $parse-mu:abort: + 670 # error("unexpected top-level command: " word-slice "\n") + 671 (write-buffered Stderr "unexpected top-level command: ") + 672 (write-buffered Stderr %edx) + 673 (write-buffered Stderr "\n") + 674 (flush Stderr) + 675 # . syscall(exit, 1) + 676 bb/copy-to-ebx 1/imm32 + 677 b8/copy-to-eax 1/imm32/exit + 678 cd/syscall 0x80/imm8 + 679 # never gets here + 680 + 681 # errors considered: + 682 # fn foo { { + 683 # fn foo { } + 684 # fn foo { } { + 685 # fn foo # no block + 686 populate-mu-function-header: # first-line : (address stream byte), out : (address function) + 687 # . prologue + 688 55/push-ebp + 689 89/<- %ebp 4/r32/esp + 690 # . save registers + 691 50/push-eax + 692 51/push-ecx + 693 57/push-edi + 694 # edi = out + 695 8b/-> *(ebp+0xc) 7/r32/edi + 696 # var word-slice/ecx : slice + 697 68/push 0/imm32/end + 698 68/push 0/imm32/start + 699 89/<- %ecx 4/r32/esp + 700 # save function name + 701 (next-word *(ebp+8) %ecx) + 702 (slice-to-string Heap %ecx) # => eax + 703 89/<- *edi 0/r32/eax + 704 # assert that next token is '{' + 705 (next-word *(ebp+8) %ecx) + 706 (slice-equal? %ecx "{") + 707 3d/compare-eax-and 0/imm32 + 708 74/jump-if-equal $populate-mu-function-header:abort/disp8 + 709 # assert that there's no further token + 710 { + 711 # word-slice = next-word(line) + 712 (next-word *(ebp+8) %ecx) + 713 # if (word-slice == '') break + 714 (slice-empty? %ecx) + 715 3d/compare-eax-and 0/imm32 + 716 75/jump-if-not-equal break/disp8 + 717 # if (slice-starts-with?(word-slice, "#")) break + 718 # . eax = *word-slice->start + 719 8b/-> *edx 0/r32/eax + 720 8a/copy-byte *eax 0/r32/AL + 721 81 4/subop/and %eax 0xff/imm32 + 722 # . if (eax == '#') break + 723 3d/compare-eax-and 0x23/imm32/hash + 724 74/jump-if-equal break/disp8 + 725 # otherwise abort + 726 eb/jump $populate-mu-function-header:abort/disp8 + 727 } + 728 $populate-mu-function-header:end: + 729 # . reclaim locals + 730 81 0/subop/add %esp 8/imm32 + 731 # . restore registers + 732 5f/pop-to-edi + 733 59/pop-to-ecx + 734 58/pop-to-eax + 735 # . epilogue + 736 89/<- %esp 5/r32/ebp + 737 5d/pop-to-ebp + 738 c3/return + 739 + 740 $populate-mu-function-header:abort: + 741 # error("function header not in form 'fn <name> {'") + 742 (write-buffered Stderr "function header not in form 'fn <name> {' -- '") + 743 (rewind-stream *(ebp+8)) + 744 (write-stream 2 *(ebp+8)) + 745 (write-buffered Stderr "'\n") + 746 (flush Stderr) + 747 # . syscall(exit, 1) + 748 bb/copy-to-ebx 1/imm32 + 749 b8/copy-to-eax 1/imm32/exit + 750 cd/syscall 0x80/imm8 + 751 # never gets here + 752 + 753 populate-mu-function-body: # in : (address buffered-file), out : (address function) + 754 # . prologue + 755 55/push-ebp + 756 89/<- %ebp 4/r32/esp + 757 # . save registers + 758 50/push-eax + 759 56/push-esi + 760 # esi = in + 761 8b/-> *(ebp+8) 6/r32/esi + 762 # var eax : (address block) = parse-mu-block(in) + 763 (parse-mu-block %esi) + 764 # out->body = eax + 765 89/<- *(eax+0x10) 0/r32/eax # Function-body + 766 $populate-mu-function-body:end: + 767 # . restore registers + 768 5e/pop-to-esi + 769 58/pop-to-eax + 770 # . epilogue + 771 89/<- %esp 5/r32/ebp + 772 5d/pop-to-ebp + 773 c3/return + 774 + 775 # parses a block, assuming that the leading '{' has already been read by the caller + 776 # errors considered: + 777 # { abc + 778 parse-mu-block: # in : (address buffered-file) -> result/eax : (address block) + 779 # . prologue + 780 55/push-ebp + 781 89/<- %ebp 4/r32/esp + 782 # . save registers + 783 50/push-eax + 784 51/push-ecx + 785 52/push-edx + 786 53/push-ebx + 787 # var line/ecx : (stream byte 512) + 788 81 5/subop/subtract %esp 0x200/imm32 + 789 68/push 0x200/imm32/length + 790 68/push 0/imm32/read + 791 68/push 0/imm32/write + 792 89/<- %ecx 4/r32/esp + 793 # var word-slice/edx : slice + 794 68/push 0/imm32/end + 795 68/push 0/imm32/start + 796 89/<- %edx 4/r32/esp + 797 # var open-curly-count/ebx : int = 1 + 798 bb/copy-to-ebx 1/imm32 + 799 { # line loop + 800 $parse-mu-block:line-loop: + 801 # if (open-curly-count == 0) break + 802 81 7/subop/compare %ebx 0/imm32 + 803 0f 84/jump-if-equal break/disp32 + 804 # line = read-line-buffered(in) + 805 (clear-stream %ecx) + 806 (read-line-buffered *(ebp+8) %ecx) + 807 # if (line->write == 0) break + 808 81 7/subop/compare *ecx 0/imm32 + 809 0f 84/jump-if-equal break/disp32 + 810 # word-slice = next-word(line) + 811 (next-word %ecx %edx) + 812 # if slice-empty?(word-slice) continue + 813 (slice-empty? %ecx) + 814 3d/compare-eax-and 0/imm32 + 815 75/jump-if-not-equal loop/disp8 + 816 # if (slice-starts-with?(word-slice, '#') continue + 817 # . eax = *word-slice->start + 818 8b/-> *edx 0/r32/eax + 819 8a/copy-byte *eax 0/r32/AL + 820 81 4/subop/and %eax 0xff/imm32 + 821 # . if (eax == '#') continue + 822 3d/compare-eax-and 0x23/imm32/hash + 823 74/jump-if-equal loop/disp8 + 824 { + 825 # if slice-equal?(word-slice, "{") ++open-curly-count + 826 { + 827 (slice-equal? %ecx "{") + 828 3d/compare-eax-and 0/imm32 + 829 74/jump-if-equal break/disp8 + 830 43/increment-ebx + 831 eb/jump $curly-found:end/disp8 + 832 } + 833 # else if slice-equal?(word-slice, "}") --open-curly-count + 834 { + 835 (slice-equal? %ecx "}") + 836 3d/compare-eax-and 0/imm32 + 837 74/jump-if-equal break/disp8 + 838 4b/decrement-ebx + 839 eb/jump $curly-found:end/disp8 + 840 } + 841 # else break + 842 eb/jump $parse-mu-block:end/disp8 + 843 } + 844 # - check for invalid tokens after curly + 845 $curly-found:end: + 846 # second-word-slice = next-word(line) + 847 (next-word %ecx %edx) + 848 # if slice-empty?(second-word-slice) continue + 849 (slice-empty? %ecx) + 850 3d/compare-eax-and 0/imm32 + 851 0f 85/jump-if-not-equal loop/disp32 + 852 # if (slice-starts-with?(second-word-slice, '#') continue + 853 # . eax = *second-word-slice->start + 854 8b/-> *edx 0/r32/eax + 855 8a/copy-byte *eax 0/r32/AL + 856 81 4/subop/and %eax 0xff/imm32 + 857 # . if (eax == '#') continue + 858 3d/compare-eax-and 0x23/imm32/hash + 859 0f 84/jump-if-equal loop/disp32 + 860 # abort + 861 eb/jump $parse-mu-block:abort/disp8 + 862 } # end line loop + 863 $parse-mu-block:end: + 864 # . reclaim locals + 865 81 0/subop/add %esp 0x214/imm32 + 866 # . restore registers + 867 5b/pop-to-ebx + 868 5a/pop-to-edx + 869 59/pop-to-ecx + 870 58/pop-to-eax + 871 # . epilogue + 872 89/<- %esp 5/r32/ebp + 873 5d/pop-to-ebp + 874 c3/return + 875 + 876 $parse-mu-block:abort: + 877 # error("'{' or '}' should be on its own line, but got '") + 878 (write-buffered Stderr "'{' or '}' should be on its own line, but got '") + 879 (rewind-stream %ecx) + 880 (write-stream 2 %ecx) + 881 (write-buffered Stderr "'\n") + 882 (flush Stderr) + 883 # . syscall(exit, 1) + 884 bb/copy-to-ebx 1/imm32 + 885 b8/copy-to-eax 1/imm32/exit + 886 cd/syscall 0x80/imm8 + 887 # never gets here + 888 + 889 check-mu-types: + 890 # . prologue + 891 55/push-ebp + 892 89/<- %ebp 4/r32/esp + 893 # + 894 $check-types:end: + 895 # . epilogue + 896 89/<- %esp 5/r32/ebp + 897 5d/pop-to-ebp + 898 c3/return + 899 + 900 emit-subx: # out : (address buffered-file) + 901 # . prologue + 902 55/push-ebp + 903 89/<- %ebp 4/r32/esp + 904 # . save registers + 905 50/push-eax + 906 51/push-ecx + 907 57/push-edi + 908 # edi = out + 909 8b/-> *(ebp+8) 7/r32/edi + 910 # var curr/ecx : (address function) = Program + 911 8b/-> *Program 1/r32/ecx + 912 { + 913 # if (curr == NULL) break + 914 81 7/subop/compare %ecx 0/imm32 + 915 0f 84/jump-if-equal break/disp32 + 916 (emit-subx-function %edi %ecx) + 917 # curr = curr->next + 918 8b/-> *(ecx+0x14) 1/r32/ecx # Function-next + 919 e9/jump loop/disp32 + 920 } + 921 $emit-subx:end: + 922 # . restore registers + 923 5f/pop-to-edi + 924 59/pop-to-ecx + 925 58/pop-to-eax + 926 # . epilogue + 927 89/<- %esp 5/r32/ebp + 928 5d/pop-to-ebp + 929 c3/return + 930 + 931 # == Emitting a function + 932 # Emit function header + 933 # Emit function prologue + 934 # Translate function body + 935 # Emit function epilogue + 936 + 937 emit-subx-function: # out : (address buffered-file), f : (address function) + 938 # . prologue + 939 55/push-ebp + 940 89/<- %ebp 4/r32/esp + 941 # . save registers + 942 50/push-eax + 943 51/push-ecx + 944 57/push-edi + 945 # edi = out + 946 8b/-> *(ebp+8) 7/r32/edi + 947 # ecx = f + 948 8b/-> *(ebp+0xc) 1/r32/ecx + 949 # + 950 (write-buffered %edi *ecx) + 951 (write-buffered %edi ":\n") + 952 (emit-subx-prologue %edi) + 953 (emit-subx-block %edi *(ecx+0x10)) # Function-body + 954 (emit-subx-epilogue %edi) + 955 $emit-subx-function:end: + 956 # . restore registers + 957 5f/pop-to-edi + 958 59/pop-to-ecx + 959 58/pop-to-eax + 960 # . epilogue + 961 89/<- %esp 5/r32/ebp + 962 5d/pop-to-ebp + 963 c3/return + 964 + 965 emit-subx-block: # out : (address buffered-file), block : (address block) + 966 # . prologue + 967 55/push-ebp + 968 89/<- %ebp 4/r32/esp + 969 # + 970 $emit-subx-block:end: + 971 # . epilogue + 972 89/<- %esp 5/r32/ebp + 973 5d/pop-to-ebp + 974 c3/return + 975 + 976 emit-subx-statement: # out : (address buffered-file), stmt : (address statement), vars : (stack var), primitives : (address primitive), functions : (address function) + 977 # . prologue + 978 55/push-ebp + 979 89/<- %ebp 4/r32/esp + 980 # . save registers + 981 50/push-eax + 982 51/push-ecx + 983 # if stmt matches a primitive, emit it + 984 { + 985 $emit-subx-statement:primitive: + 986 (find-matching-primitive *(ebp+0x14) *(ebp+0xc)) # primitives, stmt => curr/eax + 987 3d/compare-eax-and 0/imm32 + 988 74/jump-if-equal break/disp8 + 989 (emit-subx-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) # out, stmt, vars, curr + 990 e9/jump $emit-subx-statement:end/disp32 + 991 } + 992 # else if stmt matches a function, emit a call to it + 993 { + 994 $emit-subx-statement:call: + 995 (find-matching-function *(ebp+0x18) *(ebp+0xc)) # functions, stmt => curr/eax + 996 3d/compare-eax-and 0/imm32 + 997 74/jump-if-equal break/disp8 + 998 (emit-subx-call *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) # out, stmt, vars, curr + 999 e9/jump $emit-subx-statement:end/disp32 +1000 } +1001 # else abort +1002 e9/jump $emit-subx-statement:abort/disp32 +1003 $emit-subx-statement:end: +1004 # . restore registers +1005 59/pop-to-ecx +1006 58/pop-to-eax +1007 # . epilogue +1008 89/<- %esp 5/r32/ebp +1009 5d/pop-to-ebp +1010 c3/return +1011 +1012 $emit-subx-statement:abort: +1013 # error("couldn't translate '" stmt "'\n") +1014 (write-buffered Stderr "couldn't translate '") +1015 #? (emit-string Stderr *(ebp+0xc)) # TODO +1016 (write-buffered Stderr "'\n") +1017 (flush Stderr) +1018 # . syscall(exit, 1) +1019 bb/copy-to-ebx 1/imm32 +1020 b8/copy-to-eax 1/imm32/exit +1021 cd/syscall 0x80/imm8 +1022 # never gets here +1023 +1024 # Primitives supported +1025 == data +1026 Primitives: +1027 # increment var => ff 0/subop/increment *(ebp+__) +1028 "increment"/imm32/name +1029 Single-int-var-on-stack/imm32/inouts +1030 0/imm32/no-outputs +1031 "ff 0/subop/increment"/imm32/subx-name +1032 1/imm32/rm32-is-first-inout +1033 0/imm32/no-r32 +1034 0/imm32/no-imm32 +1035 _Primitive-inc-reg/imm32/next +1036 _Primitive-inc-reg: +1037 # var/reg <- increment => ff 0/subop/increment %__ +1038 "increment"/imm32/name +1039 0/imm32/no-inouts +1040 Single-int-var-in-some-register/imm32/outputs +1041 "ff 0/subop/increment"/imm32/subx-name +1042 3/imm32/rm32-is-first-output +1043 0/imm32/no-r32 +1044 0/imm32/no-imm32 +1045 _Primitive-add-reg-to-reg/imm32/next +1046 _Primitive-add-reg-to-reg: +1047 # var1/reg <- add var2/reg => 01 var1/rm32 var2/r32 +1048 "add"/imm32/name +1049 Single-int-var-in-some-register/imm32/inouts +1050 Single-int-var-in-some-register/imm32/outputs +1051 "01"/imm32/subx-name +1052 3/imm32/rm32-is-first-output +1053 1/imm32/r32-is-first-inout +1054 0/imm32/no-imm32 +1055 _Primitive-add-reg-to-mem/imm32/next +1056 _Primitive-add-reg-to-mem: +1057 # add-to var1 var2/reg => 01 var1 var2/r32 +1058 "add-to"/imm32/name +1059 Int-var-and-second-int-var-in-some-register/imm32/inouts +1060 0/imm32/outputs +1061 "01"/imm32/subx-name +1062 1/imm32/rm32-is-first-inout +1063 2/imm32/r32-is-second-inout +1064 0/imm32/no-imm32 +1065 _Primitive-add-mem-to-reg/imm32/next +1066 _Primitive-add-mem-to-reg: +1067 # var1/reg <- add var2 => 03 var2/rm32 var1/r32 +1068 "add"/imm32/name +1069 Single-int-var-on-stack/imm32/inouts +1070 Single-int-var-in-some-register/imm32/outputs +1071 "03"/imm32/subx-name +1072 1/imm32/rm32-is-first-inout +1073 3/imm32/r32-is-first-output +1074 0/imm32/no-imm32 +1075 _Primitive-add-lit-to-reg/imm32/next +1076 _Primitive-add-lit-to-reg: +1077 # var1/reg <- add lit => 81 0/subop/add var1/rm32 lit/imm32 +1078 "add"/imm32/name +1079 Single-lit-var/imm32/inouts +1080 Single-int-var-in-some-register/imm32/outputs +1081 "81 0/subop/add"/imm32/subx-name +1082 3/imm32/rm32-is-first-output +1083 0/imm32/no-r32 +1084 1/imm32/imm32-is-first-inout +1085 _Primitive-add-lit-to-mem/imm32/next +1086 _Primitive-add-lit-to-mem: +1087 # add-to var1, lit => 81 0/subop/add var1/rm32 lit/imm32 +1088 "add"/imm32/name +1089 Int-var-and-literal/imm32/inouts +1090 0/imm32/outputs +1091 "81 0/subop/add"/imm32/subx-name +1092 1/imm32/rm32-is-first-inout +1093 0/imm32/no-r32 +1094 2/imm32/imm32-is-first-inout +1095 0/imm32/next +1096 +1097 Single-int-var-on-stack: +1098 Int-var-on-stack/imm32 +1099 0/imm32/next +1100 +1101 Int-var-on-stack: +1102 "arg1"/imm32/name +1103 1/imm32/type-int +1104 1/imm32/some-block-depth +1105 1/imm32/some-stack-offset +1106 0/imm32/no-register +1107 +1108 Int-var-and-second-int-var-in-some-register: +1109 Int-var-on-stack/imm32 +1110 Int-var-in-some-register/imm32/next +1111 +1112 Int-var-and-literal: +1113 Int-var-on-stack/imm32 +1114 Single-lit-var/imm32/next 1115 -1116 $get-stmt-operand-from-arg-location:abort: -1117 # error("invalid arg-location " eax) -1118 (write-buffered Stderr "invalid arg-location ") -1119 (print-int32-buffered Stderr %eax) -1120 (write-buffered Stderr "\n") -1121 (flush Stderr) -1122 # . syscall(exit, 1) -1123 bb/copy-to-ebx 1/imm32 -1124 b8/copy-to-eax 1/imm32/exit -1125 cd/syscall 0x80/imm8 -1126 # never gets here -1127 -1128 emit-subx-r32: # out : (address buffered-file), l : arg-location, stmt : (address statement) -1129 # . prologue -1130 55/push-ebp -1131 89/<- %ebp 4/r32/esp -1132 # . save registers -1133 50/push-eax -1134 51/push-ecx -1135 # if (location == 0) return -1136 81 7/subop/compare *(ebp+0xc) 0/imm32 -1137 0f 84/jump-if-equal $emit-subx-r32:end/disp32 -1138 # -1139 (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # stmt, l => var/eax -1140 (maybe-get Registers *(eax+0x10) 8) # Var-register => eax : (address register-index) -1141 (write-buffered *(ebp+8) Space) -1142 (print-int32-buffered *(ebp+8) *eax) -1143 (write-buffered *(ebp+8) "/r32") -1144 $emit-subx-r32:end: -1145 # . restore registers -1146 59/pop-to-ecx -1147 58/pop-to-eax -1148 # . epilogue -1149 89/<- %esp 5/r32/ebp -1150 5d/pop-to-ebp -1151 c3/return -1152 -1153 emit-subx-imm32: # out : (address buffered-file), l : arg-location, stmt : (address statement) -1154 # . prologue -1155 55/push-ebp -1156 89/<- %ebp 4/r32/esp -1157 # . save registers -1158 50/push-eax -1159 51/push-ecx -1160 # if (location == 0) return -1161 81 7/subop/compare *(ebp+0xc) 0/imm32 -1162 74/jump-if-equal $emit-subx-imm32:end/disp8 -1163 # -1164 (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # stmt, l => var/eax -1165 (write-buffered *(ebp+8) Space) -1166 (write-buffered *(ebp+8) *eax) # Var-name -1167 (write-buffered *(ebp+8) "/imm32") -1168 $emit-subx-imm32:end: -1169 # . restore registers -1170 59/pop-to-ecx -1171 58/pop-to-eax -1172 # . epilogue -1173 89/<- %esp 5/r32/ebp -1174 5d/pop-to-ebp -1175 c3/return -1176 -1177 emit-subx-call: # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function) -1178 # . prologue -1179 55/push-ebp -1180 89/<- %ebp 4/r32/esp -1181 # . save registers -1182 50/push-eax -1183 51/push-ecx -1184 # -1185 (write-buffered *(ebp+8) "(") -1186 # - emit function name -1187 8b/-> *(ebp+0x14) 1/r32/ecx -1188 (write-buffered *(ebp+8) *(ecx+4)) # Function-subx-name -1189 # - emit arguments -1190 # var curr/ecx : (list var) = stmt->inouts -1191 8b/-> *(ebp+0xc) 1/r32/ecx -1192 8b/-> *(ecx+4) 1/r32/ecx # Stmt-inouts -1193 { -1194 # if (curr == null) break -1195 81 7/subop/compare %ecx 0/imm32 -1196 74/jump-if-equal break/disp8 -1197 # -1198 (emit-subx-call-operand *(ebp+8) *ecx) -1199 # curr = curr->next -1200 8b/-> *(ecx+4) 1/r32/ecx -1201 } -1202 # -1203 (write-buffered *(ebp+8) ")") -1204 $emit-subx-call:end: -1205 # . restore registers -1206 59/pop-to-ecx -1207 58/pop-to-eax -1208 # . epilogue -1209 89/<- %esp 5/r32/ebp -1210 5d/pop-to-ebp -1211 c3/return -1212 -1213 emit-subx-call-operand: # out : (address buffered-file), operand : (address variable) -1214 # . prologue -1215 55/push-ebp -1216 89/<- %ebp 4/r32/esp -1217 # . save registers -1218 50/push-eax -1219 # eax = operand -1220 8b/-> *(ebp+0xc) 0/r32/eax -1221 # if non-literal, emit appropriately -1222 (emit-subx-var-as-rm32 *(ebp+8) %eax) -1223 # else if (operand->type == literal) emit "__" -1224 { -1225 81 7/subop/compare *(eax+4) 0/imm32 # Var-type -1226 75/jump-if-not-equal break/disp8 -1227 $emit-subx-call-operand:literal: -1228 (write-buffered *(ebp+8) Space) -1229 (write-buffered *(ebp+8) *eax) -1230 } -1231 $emit-subx-call-operand:end: -1232 # . restore registers -1233 58/pop-to-eax -1234 # . epilogue -1235 89/<- %esp 5/r32/ebp -1236 5d/pop-to-ebp -1237 c3/return -1238 -1239 emit-subx-var-as-rm32: # out : (address buffered-file), operand : (address variable) -1240 # . prologue -1241 55/push-ebp -1242 89/<- %ebp 4/r32/esp -1243 # . save registers -1244 50/push-eax -1245 # eax = operand -1246 8b/-> *(ebp+0xc) 0/r32/eax -1247 # if (operand->register) emit "%__" -1248 { -1249 81 7/subop/compare *(eax+0x10) 0/imm32 # Var-register -1250 74/jump-if-equal break/disp8 -1251 $emit-subx-var-as-rm32:register: -1252 (write-buffered *(ebp+8) " %") -1253 (write-buffered *(ebp+8) *(eax+0x10)) # Var-register -1254 } -1255 # else if (operand->stack-offset) emit "*(ebp+__)" -1256 { -1257 81 7/subop/compare *(eax+0xc) 0/imm32 # Var-stack-offset -1258 74/jump-if-equal break/disp8 -1259 $emit-subx-var-as-rm32:stack: -1260 (write-buffered *(ebp+8) Space) -1261 (write-buffered *(ebp+8) "*(ebp+") -1262 8b/-> *(ebp+0xc) 0/r32/eax -1263 (print-int32-buffered *(ebp+8) *(eax+0xc)) # Var-stack-offset -1264 (write-buffered *(ebp+8) ")") -1265 } -1266 $emit-subx-var-as-rm32:end: -1267 # . restore registers -1268 58/pop-to-eax -1269 # . epilogue -1270 89/<- %esp 5/r32/ebp -1271 5d/pop-to-ebp -1272 c3/return -1273 -1274 find-matching-function: # functions : (address function), stmt : (address statement) -> result/eax : (address function) -1275 # . prologue -1276 55/push-ebp -1277 89/<- %ebp 4/r32/esp -1278 # . save registers -1279 51/push-ecx -1280 # var curr/ecx : (address function) = functions -1281 8b/-> *(ebp+8) 1/r32/ecx -1282 { -1283 # if (curr == null) break -1284 81 7/subop/compare %ecx 0/imm32 -1285 74/jump-if-equal break/disp8 -1286 # if match(curr, stmt) return curr -1287 { -1288 (mu-stmt-matches-function? *(ebp+0xc) %ecx) # => eax -1289 3d/compare-eax-and 0/imm32 -1290 74/jump-if-equal break/disp8 -1291 89/<- %eax 1/r32/ecx -1292 eb/jump $find-matching-function:end/disp8 -1293 } -1294 # curr = curr->next -1295 8b/-> *(ecx+0x10) 1/r32/ecx # Function-next -1296 eb/jump loop/disp8 -1297 } -1298 # return null -1299 b8/copy-to-eax 0/imm32 -1300 $find-matching-function:end: -1301 # . restore registers -1302 59/pop-to-ecx -1303 # . epilogue -1304 89/<- %esp 5/r32/ebp -1305 5d/pop-to-ebp -1306 c3/return -1307 -1308 find-matching-primitive: # primitives : (address primitive), stmt : (address statement) -> result/eax : (address primitive) -1309 # . prologue -1310 55/push-ebp -1311 89/<- %ebp 4/r32/esp -1312 # . save registers -1313 51/push-ecx -1314 # var curr/ecx : (address primitive) = primitives -1315 8b/-> *(ebp+8) 1/r32/ecx -1316 { -1317 $find-matching-primitive:loop: -1318 # if (curr == null) break -1319 81 7/subop/compare %ecx 0/imm32 -1320 74/jump-if-equal break/disp8 -1321 # if match(curr, stmt) return curr -1322 { -1323 (mu-stmt-matches-primitive? *(ebp+0xc) %ecx) # => eax -1324 3d/compare-eax-and 0/imm32 -1325 74/jump-if-equal break/disp8 -1326 89/<- %eax 1/r32/ecx -1327 eb/jump $find-matching-function:end/disp8 -1328 } -1329 $find-matching-primitive:next-primitive: -1330 # curr = curr->next -1331 8b/-> *(ecx+0x1c) 1/r32/ecx # Primitive-next -1332 eb/jump loop/disp8 -1333 } -1334 # return null -1335 b8/copy-to-eax 0/imm32 -1336 $find-matching-primitive:end: -1337 # . restore registers -1338 59/pop-to-ecx -1339 # . epilogue -1340 89/<- %esp 5/r32/ebp -1341 5d/pop-to-ebp -1342 c3/return -1343 -1344 mu-stmt-matches-function?: # stmt : (address statement), function : (address opcode-info) => result/eax : boolean -1345 # . prologue -1346 55/push-ebp -1347 89/<- %ebp 4/r32/esp -1348 # . save registers -1349 51/push-ecx -1350 # return primitive->name == stmt->operation -1351 8b/-> *(ebp+8) 1/r32/ecx -1352 8b/-> *(ebp+0xc) 0/r32/eax -1353 (string-equal? *ecx *eax) # => eax -1354 $mu-stmt-matches-function?:end: -1355 # . restore registers -1356 59/pop-to-ecx -1357 # . epilogue -1358 89/<- %esp 5/r32/ebp -1359 5d/pop-to-ebp -1360 c3/return -1361 -1362 mu-stmt-matches-primitive?: # stmt : (address statement), primitive : (address primitive) => result/eax : boolean -1363 # A mu stmt matches a primitive if the name matches, all the inout vars -1364 # match, and all the output vars match. -1365 # Vars match if types match and registers match. -1366 # In addition, a stmt output matches a primitive's output if types match -1367 # and the primitive has a wildcard register. -1368 # . prologue -1369 55/push-ebp -1370 89/<- %ebp 4/r32/esp -1371 # . save registers -1372 51/push-ecx -1373 52/push-edx -1374 53/push-ebx -1375 56/push-esi -1376 57/push-edi -1377 # ecx = stmt -1378 8b/-> *(ebp+8) 1/r32/ecx -1379 # edx = primitive -1380 8b/-> *(ebp+0xc) 2/r32/edx -1381 { -1382 $mu-stmt-matches-primitive?:check-name: -1383 # if (primitive->name != stmt->operation) return false -1384 (string-equal? *ecx *edx) # => eax -1385 3d/compare-eax-and 0/imm32 -1386 75/jump-if-not-equal break/disp8 -1387 b8/copy-to-eax 0/imm32 -1388 e9/jump $mu-stmt-matches-primitive?:end/disp32 -1389 } -1390 $mu-stmt-matches-primitive?:check-inouts: -1391 # curr = stmt->inouts -1392 8b/-> *(ecx+4) 6/r32/esi # Stmt-inouts -1393 # curr2 = primitive->inouts -1394 8b/-> *(edx+4) 7/r32/edi # Primitive-inouts -1395 { -1396 # if (curr == 0) return (curr2 == 0) -1397 { -1398 81 7/subop/compare %esi 0/imm32 -1399 75/jump-if-not-equal break/disp8 -1400 { -1401 81 7/subop/compare %edi 0/imm32 -1402 75/jump-if-not-equal break/disp8 -1403 # return true -1404 b8/copy-to-eax 1/imm32 -1405 e9/jump $mu-stmt-matches-primitive?:end/disp32 -1406 } -1407 # return false -1408 b8/copy-to-eax 0/imm32 -1409 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1116 Single-int-var-in-some-register: +1117 Int-var-in-some-register/imm32 +1118 0/imm32/next +1119 +1120 Int-var-in-some-register: +1121 "arg1"/imm32/name +1122 1/imm32/type-int +1123 1/imm32/some-block-depth +1124 0/imm32/no-stack-offset +1125 "*"/imm32/register +1126 +1127 Single-lit-var: +1128 Lit-var/imm32 +1129 0/imm32/next +1130 +1131 Lit-var: +1132 "literal"/imm32/name +1133 0/imm32/type-literal +1134 1/imm32/some-block-depth +1135 0/imm32/no-stack-offset +1136 0/imm32/no-register +1137 +1138 == code +1139 emit-subx-primitive: # out : (address buffered-file), stmt : (address statement), vars : (address variable), primitive : (address function) +1140 # . prologue +1141 55/push-ebp +1142 89/<- %ebp 4/r32/esp +1143 # . save registers +1144 50/push-eax +1145 51/push-ecx +1146 # ecx = primitive +1147 8b/-> *(ebp+0x14) 1/r32/ecx +1148 # emit primitive name +1149 (write-buffered *(ebp+8) *(ecx+0xc)) # Primitive-subx-name +1150 # emit rm32 if necessary +1151 (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc)) # out, Primitive-subx-rm32, stmt +1152 # emit r32 if necessary +1153 (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc)) # out, Primitive-subx-r32, stmt +1154 # emit imm32 if necessary +1155 (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc)) # out, Primitive-subx-imm32, stmt +1156 $emit-subx-primitive:end: +1157 # . restore registers +1158 59/pop-to-ecx +1159 58/pop-to-eax +1160 # . epilogue +1161 89/<- %esp 5/r32/ebp +1162 5d/pop-to-ebp +1163 c3/return +1164 +1165 emit-subx-rm32: # out : (address buffered-file), l : arg-location, stmt : (address statement) +1166 # . prologue +1167 55/push-ebp +1168 89/<- %ebp 4/r32/esp +1169 # . save registers +1170 50/push-eax +1171 # if (l == 0) return +1172 81 7/subop/compare *(ebp+0xc) 0/imm32 +1173 74/jump-if-equal $emit-subx-rm32:end/disp8 +1174 # +1175 (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # stmt, l => var/eax +1176 (emit-subx-var-as-rm32 *(ebp+8) %eax) # out, var +1177 $emit-subx-rm32:end: +1178 # . restore registers +1179 58/pop-to-eax +1180 # . epilogue +1181 89/<- %esp 5/r32/ebp +1182 5d/pop-to-ebp +1183 c3/return +1184 +1185 get-stmt-operand-from-arg-location: # stmt : (address statement), l : arg-location -> var/eax : (address variable) +1186 # . prologue +1187 55/push-ebp +1188 89/<- %ebp 4/r32/esp +1189 # . save registers +1190 51/push-ecx +1191 # eax = l +1192 8b/-> *(ebp+0xc) 0/r32/eax +1193 # ecx = stmt +1194 8b/-> *(ebp+8) 1/r32/ecx +1195 # if (l == 1) return stmt->inouts->var +1196 { +1197 3d/compare-eax-and 1/imm32 +1198 75/jump-if-not-equal break/disp8 +1199 $get-stmt-operand-from-arg-location:1: +1200 8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts +1201 8b/-> *eax 0/r32/eax # Operand-var +1202 eb/jump $get-stmt-operand-from-arg-location:end/disp8 +1203 } +1204 # if (l == 2) return stmt->inouts->next->var +1205 { +1206 3d/compare-eax-and 2/imm32 +1207 75/jump-if-not-equal break/disp8 +1208 $get-stmt-operand-from-arg-location:2: +1209 8b/-> *(ecx+8) 0/r32/eax # Stmt1-inouts +1210 8b/-> *(eax+4) 0/r32/eax # Operand-next +1211 8b/-> *eax 0/r32/eax # Operand-var +1212 eb/jump $get-stmt-operand-from-arg-location:end/disp8 +1213 } +1214 # if (l == 3) return stmt->outputs +1215 { +1216 3d/compare-eax-and 3/imm32 +1217 75/jump-if-not-equal break/disp8 +1218 $get-stmt-operand-from-arg-location:3: +1219 8b/-> *(ecx+0xc) 0/r32/eax # Stmt1-outputs +1220 8b/-> *eax 0/r32/eax # Operand-var +1221 eb/jump $get-stmt-operand-from-arg-location:end/disp8 +1222 } +1223 # abort +1224 e9/jump $get-stmt-operand-from-arg-location:abort/disp32 +1225 $get-stmt-operand-from-arg-location:end: +1226 # . restore registers +1227 59/pop-to-ecx +1228 # . epilogue +1229 89/<- %esp 5/r32/ebp +1230 5d/pop-to-ebp +1231 c3/return +1232 +1233 $get-stmt-operand-from-arg-location:abort: +1234 # error("invalid arg-location " eax) +1235 (write-buffered Stderr "invalid arg-location ") +1236 (print-int32-buffered Stderr %eax) +1237 (write-buffered Stderr "\n") +1238 (flush Stderr) +1239 # . syscall(exit, 1) +1240 bb/copy-to-ebx 1/imm32 +1241 b8/copy-to-eax 1/imm32/exit +1242 cd/syscall 0x80/imm8 +1243 # never gets here +1244 +1245 emit-subx-r32: # out : (address buffered-file), l : arg-location, stmt : (address statement) +1246 # . prologue +1247 55/push-ebp +1248 89/<- %ebp 4/r32/esp +1249 # . save registers +1250 50/push-eax +1251 51/push-ecx +1252 # if (location == 0) return +1253 81 7/subop/compare *(ebp+0xc) 0/imm32 +1254 0f 84/jump-if-equal $emit-subx-r32:end/disp32 +1255 # +1256 (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # stmt, l => var/eax +1257 (maybe-get Registers *(eax+0x10) 8) # Var-register => eax : (address register-index) +1258 (write-buffered *(ebp+8) Space) +1259 (print-int32-buffered *(ebp+8) *eax) +1260 (write-buffered *(ebp+8) "/r32") +1261 $emit-subx-r32:end: +1262 # . restore registers +1263 59/pop-to-ecx +1264 58/pop-to-eax +1265 # . epilogue +1266 89/<- %esp 5/r32/ebp +1267 5d/pop-to-ebp +1268 c3/return +1269 +1270 emit-subx-imm32: # out : (address buffered-file), l : arg-location, stmt : (address statement) +1271 # . prologue +1272 55/push-ebp +1273 89/<- %ebp 4/r32/esp +1274 # . save registers +1275 50/push-eax +1276 51/push-ecx +1277 # if (location == 0) return +1278 81 7/subop/compare *(ebp+0xc) 0/imm32 +1279 74/jump-if-equal $emit-subx-imm32:end/disp8 +1280 # +1281 (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # stmt, l => var/eax +1282 (write-buffered *(ebp+8) Space) +1283 (write-buffered *(ebp+8) *eax) # Var-name +1284 (write-buffered *(ebp+8) "/imm32") +1285 $emit-subx-imm32:end: +1286 # . restore registers +1287 59/pop-to-ecx +1288 58/pop-to-eax +1289 # . epilogue +1290 89/<- %esp 5/r32/ebp +1291 5d/pop-to-ebp +1292 c3/return +1293 +1294 emit-subx-call: # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function) +1295 # . prologue +1296 55/push-ebp +1297 89/<- %ebp 4/r32/esp +1298 # . save registers +1299 50/push-eax +1300 51/push-ecx +1301 # +1302 (write-buffered *(ebp+8) "(") +1303 # - emit function name +1304 8b/-> *(ebp+0x14) 1/r32/ecx +1305 (write-buffered *(ebp+8) *(ecx+4)) # Function-subx-name +1306 # - emit arguments +1307 # var curr/ecx : (list var) = stmt->inouts +1308 8b/-> *(ebp+0xc) 1/r32/ecx +1309 8b/-> *(ecx+8) 1/r32/ecx # Stmt1-inouts +1310 { +1311 # if (curr == null) break +1312 81 7/subop/compare %ecx 0/imm32 +1313 74/jump-if-equal break/disp8 +1314 # +1315 (emit-subx-call-operand *(ebp+8) *ecx) +1316 # curr = curr->next +1317 8b/-> *(ecx+4) 1/r32/ecx +1318 } +1319 # +1320 (write-buffered *(ebp+8) ")") +1321 $emit-subx-call:end: +1322 # . restore registers +1323 59/pop-to-ecx +1324 58/pop-to-eax +1325 # . epilogue +1326 89/<- %esp 5/r32/ebp +1327 5d/pop-to-ebp +1328 c3/return +1329 +1330 emit-subx-call-operand: # out : (address buffered-file), operand : (address variable) +1331 # . prologue +1332 55/push-ebp +1333 89/<- %ebp 4/r32/esp +1334 # . save registers +1335 50/push-eax +1336 # eax = operand +1337 8b/-> *(ebp+0xc) 0/r32/eax +1338 # if non-literal, emit appropriately +1339 (emit-subx-var-as-rm32 *(ebp+8) %eax) +1340 # else if (operand->type == literal) emit "__" +1341 { +1342 81 7/subop/compare *(eax+4) 0/imm32 # Var-type +1343 75/jump-if-not-equal break/disp8 +1344 $emit-subx-call-operand:literal: +1345 (write-buffered *(ebp+8) Space) +1346 (write-buffered *(ebp+8) *eax) +1347 } +1348 $emit-subx-call-operand:end: +1349 # . restore registers +1350 58/pop-to-eax +1351 # . epilogue +1352 89/<- %esp 5/r32/ebp +1353 5d/pop-to-ebp +1354 c3/return +1355 +1356 emit-subx-var-as-rm32: # out : (address buffered-file), operand : (address variable) +1357 # . prologue +1358 55/push-ebp +1359 89/<- %ebp 4/r32/esp +1360 # . save registers +1361 50/push-eax +1362 # eax = operand +1363 8b/-> *(ebp+0xc) 0/r32/eax +1364 # if (operand->register) emit "%__" +1365 { +1366 81 7/subop/compare *(eax+0x10) 0/imm32 # Var-register +1367 74/jump-if-equal break/disp8 +1368 $emit-subx-var-as-rm32:register: +1369 (write-buffered *(ebp+8) " %") +1370 (write-buffered *(ebp+8) *(eax+0x10)) # Var-register +1371 } +1372 # else if (operand->stack-offset) emit "*(ebp+__)" +1373 { +1374 81 7/subop/compare *(eax+0xc) 0/imm32 # Var-stack-offset +1375 74/jump-if-equal break/disp8 +1376 $emit-subx-var-as-rm32:stack: +1377 (write-buffered *(ebp+8) Space) +1378 (write-buffered *(ebp+8) "*(ebp+") +1379 8b/-> *(ebp+0xc) 0/r32/eax +1380 (print-int32-buffered *(ebp+8) *(eax+0xc)) # Var-stack-offset +1381 (write-buffered *(ebp+8) ")") +1382 } +1383 $emit-subx-var-as-rm32:end: +1384 # . restore registers +1385 58/pop-to-eax +1386 # . epilogue +1387 89/<- %esp 5/r32/ebp +1388 5d/pop-to-ebp +1389 c3/return +1390 +1391 find-matching-function: # functions : (address function), stmt : (address statement) -> result/eax : (address function) +1392 # . prologue +1393 55/push-ebp +1394 89/<- %ebp 4/r32/esp +1395 # . save registers +1396 51/push-ecx +1397 # var curr/ecx : (address function) = functions +1398 8b/-> *(ebp+8) 1/r32/ecx +1399 { +1400 # if (curr == null) break +1401 81 7/subop/compare %ecx 0/imm32 +1402 74/jump-if-equal break/disp8 +1403 # if match(curr, stmt) return curr +1404 { +1405 (mu-stmt-matches-function? *(ebp+0xc) %ecx) # => eax +1406 3d/compare-eax-and 0/imm32 +1407 74/jump-if-equal break/disp8 +1408 89/<- %eax 1/r32/ecx +1409 eb/jump $find-matching-function:end/disp8 1410 } -1411 # if (curr2 == 0) return false -1412 { -1413 81 7/subop/compare %edi 0/imm32 -1414 75/jump-if-not-equal break/disp8 -1415 b8/copy-to-eax 0/imm32 -1416 e9/jump $mu-stmt-matches-primitive?:end/disp32 -1417 } -1418 # if (curr != curr2) return false -1419 { -1420 (operand-matches-primitive? *esi *edi) # => eax -1421 3d/compare-eax-and 0/imm32 -1422 75/jump-if-not-equal break/disp8 -1423 b8/copy-to-eax 0/imm32 -1424 e9/jump $mu-stmt-matches-primitive?:end/disp32 -1425 } -1426 # curr=curr->next -1427 8b/-> *(ecx+4) 1/r32/ecx # Operand-next -1428 # curr2=curr2->next -1429 8b/-> *(edx+4) 2/r32/edx # Operand-next -1430 } -1431 $mu-stmt-matches-primitive?:check-outputs: -1432 # ecx = stmt -1433 8b/-> *(ebp+8) 1/r32/ecx -1434 # edx = primitive -1435 8b/-> *(ebp+0xc) 2/r32/edx -1436 # curr = stmt->outputs -1437 8b/-> *(ecx+8) 6/r32/esi # Stmt-outputs -1438 # curr2 = primitive->outputs -1439 8b/-> *(edx+8) 7/r32/edi # Primitive-outputs -1440 { -1441 # if (curr == 0) return (curr2 == 0) -1442 { -1443 81 7/subop/compare %esi 0/imm32 -1444 75/jump-if-not-equal break/disp8 -1445 { -1446 81 7/subop/compare %edi 0/imm32 -1447 75/jump-if-not-equal break/disp8 -1448 # return true -1449 b8/copy-to-eax 1/imm32 -1450 e9/jump $mu-stmt-matches-primitive?:end/disp32 -1451 } -1452 # return false -1453 b8/copy-to-eax 0/imm32 -1454 e9/jump $mu-stmt-matches-primitive?:end/disp32 -1455 } -1456 # if (curr2 == 0) return false -1457 { -1458 81 7/subop/compare %edi 0/imm32 -1459 75/jump-if-not-equal break/disp8 -1460 b8/copy-to-eax 0/imm32 -1461 e9/jump $mu-stmt-matches-primitive?:end/disp32 -1462 } -1463 # if (curr != curr2) return false -1464 { -1465 (operand-matches-primitive? *esi *edi) # => eax -1466 3d/compare-eax-and 0/imm32 -1467 75/jump-if-not-equal break/disp8 -1468 b8/copy-to-eax 0/imm32 -1469 e9/jump $mu-stmt-matches-primitive?:end/disp32 -1470 } -1471 # curr=curr->next -1472 8b/-> *(ecx+4) 1/r32/ecx # Operand-next -1473 # curr2=curr2->next -1474 8b/-> *(edx+4) 2/r32/edx # Operand-next -1475 } -1476 $mu-stmt-matches-primitive?:return-true: -1477 b8/copy-to-eax 1/imm32 -1478 $mu-stmt-matches-primitive?:end: -1479 # . restore registers -1480 5f/pop-to-edi -1481 5e/pop-to-esi -1482 5b/pop-to-ebx -1483 5a/pop-to-edx -1484 59/pop-to-ecx -1485 # . epilogue -1486 89/<- %esp 5/r32/ebp -1487 5d/pop-to-ebp -1488 c3/return -1489 -1490 operand-matches-primitive?: # var : (address var), primout-var : (address var) => result/eax : boolean -1491 # . prologue -1492 55/push-ebp -1493 89/<- %ebp 4/r32/esp -1494 # . save registers -1495 56/push-esi -1496 57/push-edi -1497 # esi = var -1498 8b/-> *(ebp+8) 6/r32/esi -1499 # edi = primout-var -1500 8b/-> *(ebp+0xc) 7/r32/edi -1501 # if (var->type != primout-var->type) return false -1502 # TODO -1503 # return false if var->register doesn't match primout-var->register -1504 { -1505 # if addresses are equal, don't return here -1506 8b/-> *(esi+0x10) 0/r32/eax -1507 39/compare *(edi+0x10) 0/r32/eax -1508 74/jump-if-equal break/disp8 -1509 # if either address is 0, return false -1510 3d/compare-eax-and 0/imm32 -1511 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result -1512 81 7/subop/compare *(edi+0x10) 0/imm32 -1513 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result -1514 # if primout-var->register is "*", return true -1515 (string-equal? *(edi+0x10) "*") # Var-register -1516 3d/compare-eax-and 0/imm32 -1517 b8/copy-to-eax 1/imm32/true -1518 75/jump-if-not-equal $operand-matches-primitive?:end/disp8 -1519 # if string contents don't match, return false -1520 (string-equal? *(esi+0x10) *(edi+0x10)) # Var-register Var-register -1521 3d/compare-eax-and 0/imm32 -1522 b8/copy-to-eax 0/imm32/false -1523 74/jump-if-equal $operand-matches-primitive?:end/disp8 -1524 } -1525 # return true -1526 b8/copy-to-eax 1/imm32/true -1527 $operand-matches-primitive?:end: -1528 # . restore registers -1529 5f/pop-to-edi -1530 5e/pop-to-esi -1531 # . epilogue -1532 89/<- %esp 5/r32/ebp -1533 5d/pop-to-ebp -1534 c3/return -1535 -1536 test-emit-subx-statement-primitive: -1537 # Primitive operation on a variable on the stack. -1538 # increment foo -1539 # => -1540 # ff 0/subop/increment *(ebp-8) -1541 # -1542 # There's a variable on the var stack as follows: -1543 # name: 'foo' -1544 # type: int -1545 # stack-offset: -8 -1546 # -1547 # There's a primitive with this info: -1548 # name: 'increment' -1549 # inouts: int/mem -1550 # value: 'ff 0/subop/increment' -1551 # -1552 # There's nothing in functions. -1553 # -1554 # . prologue -1555 55/push-ebp -1556 89/<- %ebp 4/r32/esp -1557 # setup -1558 (clear-stream _test-output-stream) -1559 (clear-stream _test-output-buffered-file->buffer) -1560 # var-foo/ecx : var -1561 68/push 0/imm32/no-register -1562 68/push -8/imm32/stack-offset -1563 68/push 1/imm32/block-depth -1564 68/push 1/imm32/type-int -1565 68/push "foo"/imm32 -1566 89/<- %ecx 4/r32/esp -1567 #? $aa-var-in-ecx: -1568 # vars/edx : (stack 1) -1569 51/push-ecx/var-foo -1570 68/push 1/imm32/data-length -1571 68/push 1/imm32/top -1572 89/<- %edx 4/r32/esp -1573 #? $aa-vars-in-edx: -1574 # operand/ebx : (list var) -1575 68/push 0/imm32/next -1576 51/push-ecx/var-foo -1577 89/<- %ebx 4/r32/esp -1578 #? $aa-stmt-operand-in-ebx: -1579 # stmt/esi : statement -1580 68/push 0/imm32/next -1581 68/push 0/imm32/outputs -1582 53/push-ebx/operands -1583 68/push "increment"/imm32/operation -1584 89/<- %esi 4/r32/esp -1585 #? $aa-stmt-in-esi: -1586 # primitives/ebx : primitive -1587 68/push 0/imm32/next -1588 68/push 0/imm32/no-imm32 -1589 68/push 0/imm32/no-r32 -1590 68/push 1/imm32/rm32-is-first-inout -1591 68/push "ff 0/subop/increment"/imm32/subx-name -1592 68/push 0/imm32/outputs -1593 53/push-ebx/inouts # hack; in practice we won't have the same var in function definition and call -1594 68/push "increment"/imm32/name -1595 89/<- %ebx 4/r32/esp -1596 $aa-primitive-in-ebx: -1597 # convert -1598 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) -1599 (flush _test-output-buffered-file) -1600 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -1606 # check output -1607 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive") -1608 # . epilogue -1609 89/<- %esp 5/r32/ebp -1610 5d/pop-to-ebp -1611 c3/return -1612 -1613 test-emit-subx-statement-primitive-register: -1614 # Primitive operation on a variable in a register. -1615 # foo <- increment -1616 # => -1617 # ff 0/subop/increment %eax # sub-optimal, but should suffice -1618 # -1619 # There's a variable on the var stack as follows: -1620 # name: 'foo' -1621 # type: int -1622 # register: 'eax' -1623 # -1624 # There's a primitive with this info: -1625 # name: 'increment' -1626 # out: int/reg -1627 # value: 'ff 0/subop/increment' -1628 # -1629 # There's nothing in functions. -1630 # -1631 # . prologue -1632 55/push-ebp -1633 89/<- %ebp 4/r32/esp -1634 # setup -1635 (clear-stream _test-output-stream) -1636 (clear-stream _test-output-buffered-file->buffer) -1637 # var-foo/ecx : var in eax -1638 68/push "eax"/imm32/register -1639 68/push 0/imm32/no-stack-offset -1640 68/push 1/imm32/block-depth -1641 68/push 1/imm32/type-int -1642 68/push "foo"/imm32 -1643 89/<- %ecx 4/r32/esp -1644 # vars/edx : (stack 1) -1645 51/push-ecx/var-foo -1646 68/push 1/imm32/data-length -1647 68/push 1/imm32/top -1648 89/<- %edx 4/r32/esp -1649 # operand/ebx : (list var) -1650 68/push 0/imm32/next -1651 51/push-ecx/var-foo -1652 89/<- %ebx 4/r32/esp -1653 # stmt/esi : statement -1654 68/push 0/imm32/next -1655 53/push-ebx/outputs -1656 68/push 0/imm32/inouts -1657 68/push "increment"/imm32/operation -1658 89/<- %esi 4/r32/esp -1659 # formal-var/ebx : var in any register -1660 68/push Any-register/imm32 -1661 68/push 0/imm32/no-stack-offset -1662 68/push 1/imm32/block-depth -1663 68/push 1/imm32/type-int -1664 68/push "dummy"/imm32 -1665 89/<- %ebx 4/r32/esp -1666 # operand/ebx : (list var) -1667 68/push 0/imm32/next -1668 53/push-ebx/formal-var -1669 89/<- %ebx 4/r32/esp -1670 # primitives/ebx : primitive -1671 68/push 0/imm32/next -1672 68/push 0/imm32/no-imm32 -1673 68/push 0/imm32/no-r32 -1674 68/push 3/imm32/rm32-in-first-output -1675 68/push "ff 0/subop/increment"/imm32/subx-name -1676 53/push-ebx/outputs -1677 68/push 0/imm32/inouts -1678 68/push "increment"/imm32/name -1679 89/<- %ebx 4/r32/esp -1680 # convert -1681 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) -1682 (flush _test-output-buffered-file) -1683 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -1689 # check output -1690 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register") -1691 # . epilogue -1692 89/<- %esp 5/r32/ebp -1693 5d/pop-to-ebp -1694 c3/return -1695 -1696 test-emit-subx-statement-select-primitive: -1697 # Select the right primitive between overloads. -1698 # foo <- increment -1699 # => -1700 # ff 0/subop/increment %eax # sub-optimal, but should suffice -1701 # -1702 # There's a variable on the var stack as follows: -1703 # name: 'foo' -1704 # type: int -1705 # register: 'eax' -1706 # -1707 # There's two primitives, as follows: -1708 # - name: 'increment' -1709 # out: int/reg -1710 # value: 'ff 0/subop/increment' -1711 # - name: 'increment' -1712 # inout: int/mem -1713 # value: 'ff 0/subop/increment' -1714 # -1715 # There's nothing in functions. -1716 # -1717 # . prologue -1718 55/push-ebp -1719 89/<- %ebp 4/r32/esp -1720 # setup -1721 (clear-stream _test-output-stream) -1722 (clear-stream _test-output-buffered-file->buffer) -1723 # var-foo/ecx : var in eax -1724 68/push "eax"/imm32/register -1725 68/push 0/imm32/no-stack-offset -1726 68/push 1/imm32/block-depth -1727 68/push 1/imm32/type-int -1728 68/push "foo"/imm32 -1729 89/<- %ecx 4/r32/esp -1730 # vars/edx : (stack 1) -1731 51/push-ecx/var-foo -1732 68/push 1/imm32/data-length -1733 68/push 1/imm32/top -1734 89/<- %edx 4/r32/esp -1735 # real-outputs/edi : (list var) -1736 68/push 0/imm32/next -1737 51/push-ecx/var-foo -1738 89/<- %edi 4/r32/esp -1739 # stmt/esi : statement -1740 68/push 0/imm32/next -1741 57/push-edi/outputs -1742 68/push 0/imm32/inouts -1743 68/push "increment"/imm32/operation -1744 89/<- %esi 4/r32/esp -1745 # formal-var/ebx : var in any register -1746 68/push Any-register/imm32 -1747 68/push 0/imm32/no-stack-offset -1748 68/push 1/imm32/block-depth -1749 68/push 1/imm32/type-int -1750 68/push "dummy"/imm32 -1751 89/<- %ebx 4/r32/esp -1752 # formal-outputs/ebx : (list var) -1753 68/push 0/imm32/next -1754 53/push-ebx/formal-var -1755 89/<- %ebx 4/r32/esp -1756 # primitive1/ebx : primitive -1757 68/push 0/imm32/next -1758 68/push 0/imm32/no-imm32 -1759 68/push 0/imm32/no-r32 -1760 68/push 3/imm32/rm32-in-first-output -1761 68/push "ff 0/subop/increment"/imm32/subx-name -1762 53/push-ebx/outputs/formal-outputs -1763 68/push 0/imm32/inouts -1764 68/push "increment"/imm32/name -1765 89/<- %ebx 4/r32/esp -1766 # primitives/ebx : primitive -1767 53/push-ebx/next -1768 68/push 0/imm32/no-imm32 -1769 68/push 0/imm32/no-r32 -1770 68/push 1/imm32/rm32-is-first-inout -1771 68/push "ff 0/subop/increment"/imm32/subx-name -1772 68/push 0/imm32/outputs -1773 57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call -1774 68/push "increment"/imm32/name -1775 89/<- %ebx 4/r32/esp -1776 # convert -1777 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) -1778 (flush _test-output-buffered-file) -1779 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -1785 # check output -1786 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive") -1787 # . epilogue -1788 89/<- %esp 5/r32/ebp -1789 5d/pop-to-ebp -1790 c3/return -1791 -1792 test-emit-subx-statement-select-primitive-2: -1793 # Select the right primitive between overloads. -1794 # foo <- increment -1795 # => -1796 # ff 0/subop/increment %eax # sub-optimal, but should suffice -1797 # -1798 # There's a variable on the var stack as follows: -1799 # name: 'foo' -1800 # type: int -1801 # register: 'eax' -1802 # -1803 # There's two primitives, as follows: -1804 # - name: 'increment' -1805 # out: int/reg -1806 # value: 'ff 0/subop/increment' -1807 # - name: 'increment' -1808 # inout: int/mem -1809 # value: 'ff 0/subop/increment' -1810 # -1811 # There's nothing in functions. -1812 # -1813 # . prologue -1814 55/push-ebp -1815 89/<- %ebp 4/r32/esp -1816 # setup -1817 (clear-stream _test-output-stream) -1818 (clear-stream _test-output-buffered-file->buffer) -1819 # var-foo/ecx : var in eax -1820 68/push "eax"/imm32/register -1821 68/push 0/imm32/no-stack-offset -1822 68/push 1/imm32/block-depth -1823 68/push 1/imm32/type-int -1824 68/push "foo"/imm32 -1825 89/<- %ecx 4/r32/esp -1826 # vars/edx : (stack 1) -1827 51/push-ecx/var-foo -1828 68/push 1/imm32/data-length -1829 68/push 1/imm32/top -1830 89/<- %edx 4/r32/esp -1831 # inouts/edi : (list var) -1832 68/push 0/imm32/next -1833 51/push-ecx/var-foo -1834 89/<- %edi 4/r32/esp -1835 # stmt/esi : statement -1836 68/push 0/imm32/next -1837 68/push 0/imm32/outputs -1838 57/push-edi/inouts -1839 68/push "increment"/imm32/operation -1840 89/<- %esi 4/r32/esp -1841 # formal-var/ebx : var in any register -1842 68/push Any-register/imm32 -1843 68/push 0/imm32/no-stack-offset -1844 68/push 1/imm32/block-depth -1845 68/push 1/imm32/type-int -1846 68/push "dummy"/imm32 -1847 89/<- %ebx 4/r32/esp -1848 # operand/ebx : (list var) -1849 68/push 0/imm32/next -1850 53/push-ebx/formal-var -1851 89/<- %ebx 4/r32/esp -1852 # primitive1/ebx : primitive -1853 68/push 0/imm32/next -1854 68/push 0/imm32/no-imm32 -1855 68/push 0/imm32/no-r32 -1856 68/push 3/imm32/rm32-in-first-output -1857 68/push "ff 0/subop/increment"/imm32/subx-name -1858 53/push-ebx/outputs/formal-outputs -1859 68/push 0/imm32/inouts -1860 68/push "increment"/imm32/name -1861 89/<- %ebx 4/r32/esp -1862 # primitives/ebx : primitive -1863 53/push-ebx/next -1864 68/push 0/imm32/no-imm32 -1865 68/push 0/imm32/no-r32 -1866 68/push 1/imm32/rm32-is-first-inout -1867 68/push "ff 0/subop/increment"/imm32/subx-name -1868 68/push 0/imm32/outputs -1869 57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call -1870 68/push "increment"/imm32/name -1871 89/<- %ebx 4/r32/esp -1872 # convert -1873 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) -1874 (flush _test-output-buffered-file) -1875 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -1881 # check output -1882 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2") -1883 # . epilogue -1884 89/<- %esp 5/r32/ebp -1885 5d/pop-to-ebp -1886 c3/return -1887 -1888 test-increment-register: -1889 # Select the right primitive between overloads. -1890 # foo <- increment -1891 # => -1892 # ff 0/subop/increment %eax # sub-optimal, but should suffice -1893 # -1894 # There's a variable on the var stack as follows: -1895 # name: 'foo' -1896 # type: int -1897 # register: 'eax' -1898 # -1899 # Primitives are the global definitions. -1900 # -1901 # There are no functions defined. -1902 # -1903 # . prologue -1904 55/push-ebp -1905 89/<- %ebp 4/r32/esp -1906 # setup -1907 (clear-stream _test-output-stream) -1908 (clear-stream _test-output-buffered-file->buffer) -1909 # var-foo/ecx : var in eax -1910 68/push "eax"/imm32/register -1911 68/push 0/imm32/no-stack-offset -1912 68/push 1/imm32/block-depth -1913 68/push 1/imm32/type-int -1914 68/push "foo"/imm32 -1915 89/<- %ecx 4/r32/esp -1916 # vars/edx : (stack 1) -1917 51/push-ecx/var-foo -1918 68/push 1/imm32/data-length -1919 68/push 1/imm32/top -1920 89/<- %edx 4/r32/esp -1921 # real-outputs/edi : (list var) -1922 68/push 0/imm32/next -1923 51/push-ecx/var-foo -1924 89/<- %edi 4/r32/esp -1925 # stmt/esi : statement -1926 68/push 0/imm32/next -1927 57/push-edi/outputs -1928 68/push 0/imm32/inouts -1929 68/push "increment"/imm32/operation -1930 89/<- %esi 4/r32/esp -1931 # convert -1932 (emit-subx-statement _test-output-buffered-file %esi %edx Primitives 0) -1933 (flush _test-output-buffered-file) -1934 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -1940 # check output -1941 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-increment-register") -1942 # . epilogue -1943 89/<- %esp 5/r32/ebp -1944 5d/pop-to-ebp -1945 c3/return -1946 -1947 test-increment-var: -1948 # Select the right primitive between overloads. -1949 # foo <- increment -1950 # => -1951 # ff 0/subop/increment %eax # sub-optimal, but should suffice -1952 # -1953 # There's a variable on the var stack as follows: -1954 # name: 'foo' -1955 # type: int -1956 # register: 'eax' -1957 # -1958 # Primitives are the global definitions. -1959 # -1960 # There are no functions defined. -1961 # -1962 # . prologue -1963 55/push-ebp -1964 89/<- %ebp 4/r32/esp -1965 # setup -1966 (clear-stream _test-output-stream) -1967 (clear-stream _test-output-buffered-file->buffer) -1968 # var-foo/ecx : var in eax -1969 68/push "eax"/imm32/register -1970 68/push 0/imm32/no-stack-offset -1971 68/push 1/imm32/block-depth -1972 68/push 1/imm32/type-int -1973 68/push "foo"/imm32 -1974 89/<- %ecx 4/r32/esp -1975 # vars/edx : (stack 1) -1976 51/push-ecx/var-foo -1977 68/push 1/imm32/data-length -1978 68/push 1/imm32/top -1979 89/<- %edx 4/r32/esp -1980 # inouts/edi : (list var) -1981 68/push 0/imm32/next -1982 51/push-ecx/var-foo -1983 89/<- %edi 4/r32/esp -1984 # stmt/esi : statement -1985 68/push 0/imm32/next -1986 68/push 0/imm32/outputs -1987 57/push-edi/inouts -1988 68/push "increment"/imm32/operation -1989 89/<- %esi 4/r32/esp -1990 # convert -1991 (emit-subx-statement _test-output-buffered-file %esi %edx Primitives 0) -1992 (flush _test-output-buffered-file) -1993 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -1999 # check output -2000 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-increment-var") -2001 # . epilogue -2002 89/<- %esp 5/r32/ebp -2003 5d/pop-to-ebp -2004 c3/return -2005 -2006 test-add-reg-to-reg: -2007 # var1/reg <- add var2/reg -2008 # => -2009 # 01 %var1 var2 -2010 # -2011 # . prologue -2012 55/push-ebp -2013 89/<- %ebp 4/r32/esp -2014 # setup -2015 (clear-stream _test-output-stream) -2016 (clear-stream _test-output-buffered-file->buffer) -2017 # var-var1/ecx : var in eax -2018 68/push "eax"/imm32/register -2019 68/push 0/imm32/no-stack-offset -2020 68/push 1/imm32/block-depth -2021 68/push 1/imm32/type-int -2022 68/push "var1"/imm32 -2023 89/<- %ecx 4/r32/esp -2024 # var-var2/edx : var in ecx -2025 68/push "ecx"/imm32/register -2026 68/push 0/imm32/no-stack-offset -2027 68/push 1/imm32/block-depth -2028 68/push 1/imm32/type-int -2029 68/push "var2"/imm32 -2030 89/<- %edx 4/r32/esp -2031 # inouts/esi : (list var2) -2032 68/push 0/imm32/next -2033 52/push-edx/var-var2 -2034 89/<- %esi 4/r32/esp -2035 # outputs/edi : (list var1) -2036 68/push 0/imm32/next -2037 51/push-ecx/var-var1 -2038 89/<- %edi 4/r32/esp -2039 # stmt/esi : statement -2040 68/push 0/imm32/next -2041 57/push-edi/outputs -2042 56/push-esi/inouts -2043 68/push "add"/imm32/operation -2044 89/<- %esi 4/r32/esp -2045 # convert -2046 (emit-subx-statement _test-output-buffered-file %esi 0 Primitives 0) -2047 (flush _test-output-buffered-file) -2048 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -2054 # check output -2055 (check-next-stream-line-equal _test-output-stream "01 %eax 0x00000001/r32" "F - test-add-reg-to-reg") -2056 # . epilogue -2057 89/<- %esp 5/r32/ebp -2058 5d/pop-to-ebp -2059 c3/return -2060 -2061 test-add-literal-to-reg: -2062 # var1/eax <- add 0x34 -2063 # => -2064 # 81 0/subop/add %eax 0x34/imm32 -2065 # -2066 # . prologue -2067 55/push-ebp -2068 89/<- %ebp 4/r32/esp -2069 # setup -2070 (clear-stream _test-output-stream) -2071 (clear-stream _test-output-buffered-file->buffer) -2072 # var-var1/ecx : var in eax -2073 68/push "eax"/imm32/register -2074 68/push 0/imm32/no-stack-offset -2075 68/push 1/imm32/block-depth -2076 68/push 1/imm32/type-int -2077 68/push "var1"/imm32 -2078 89/<- %ecx 4/r32/esp -2079 # var-var2/edx : var literal -2080 68/push 0/imm32/no-register -2081 68/push 0/imm32/no-stack-offset -2082 68/push 1/imm32/block-depth -2083 68/push 0/imm32/type-literal -2084 68/push "0x34"/imm32 -2085 89/<- %edx 4/r32/esp -2086 # inouts/esi : (list var2) -2087 68/push 0/imm32/next -2088 52/push-edx/var-var2 -2089 89/<- %esi 4/r32/esp -2090 # outputs/edi : (list var1) -2091 68/push 0/imm32/next -2092 51/push-ecx/var-var1 -2093 89/<- %edi 4/r32/esp -2094 # stmt/esi : statement -2095 68/push 0/imm32/next -2096 57/push-edi/outputs -2097 56/push-esi/inouts -2098 68/push "add"/imm32/operation -2099 89/<- %esi 4/r32/esp -2100 # convert -2101 (emit-subx-statement _test-output-buffered-file %esi 0 Primitives 0) -2102 (flush _test-output-buffered-file) -2103 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -2109 # check output -2110 (check-next-stream-line-equal _test-output-stream "81 0/subop/add %eax 0x34/imm32" "F - test-add-literal-to-reg") -2111 # . epilogue -2112 89/<- %esp 5/r32/ebp -2113 5d/pop-to-ebp -2114 c3/return -2115 -2116 test-emit-subx-statement-function-call: -2117 # Call a function on a variable on the stack. -2118 # f foo -2119 # => -2120 # (f2 *(ebp-8)) -2121 # (Changing the function name supports overloading in general, but here it -2122 # just serves to help disambiguate things.) -2123 # -2124 # There's a variable on the var stack as follows: -2125 # name: 'foo' -2126 # type: int -2127 # stack-offset: -8 -2128 # -2129 # There's nothing in primitives. -2130 # -2131 # There's a function with this info: -2132 # name: 'f' -2133 # inout: int/mem -2134 # value: 'f2' -2135 # -2136 # . prologue -2137 55/push-ebp -2138 89/<- %ebp 4/r32/esp -2139 # setup -2140 (clear-stream _test-output-stream) -2141 (clear-stream _test-output-buffered-file->buffer) -2142 # var-foo/ecx : var -2143 68/push 0/imm32/no-register -2144 68/push -8/imm32/stack-offset -2145 68/push 0/imm32/block-depth -2146 68/push 1/imm32/type-int -2147 68/push "foo"/imm32 -2148 89/<- %ecx 4/r32/esp -2149 # vars/edx = (stack 1) -2150 51/push-ecx/var-foo -2151 68/push 1/imm32/data-length -2152 68/push 1/imm32/top -2153 89/<- %edx 4/r32/esp -2154 # operands/esi : (list var) -2155 68/push 0/imm32/next -2156 51/push-ecx/var-foo -2157 89/<- %esi 4/r32/esp -2158 # stmt/esi : statement -2159 68/push 0/imm32/next -2160 68/push 0/imm32/outputs -2161 56/push-esi/inouts -2162 68/push "f"/imm32/operation -2163 89/<- %esi 4/r32/esp -2164 # functions/ebx : function -2165 68/push 0/imm32/next -2166 68/push 0/imm32/body -2167 68/push 0/imm32/outputs -2168 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call -2169 68/push "f2"/imm32/subx-name -2170 68/push "f"/imm32/name -2171 89/<- %ebx 4/r32/esp +1411 # curr = curr->next +1412 8b/-> *(ecx+0x10) 1/r32/ecx # Function-next +1413 eb/jump loop/disp8 +1414 } +1415 # return null +1416 b8/copy-to-eax 0/imm32 +1417 $find-matching-function:end: +1418 # . restore registers +1419 59/pop-to-ecx +1420 # . epilogue +1421 89/<- %esp 5/r32/ebp +1422 5d/pop-to-ebp +1423 c3/return +1424 +1425 find-matching-primitive: # primitives : (address primitive), stmt : (address statement) -> result/eax : (address primitive) +1426 # . prologue +1427 55/push-ebp +1428 89/<- %ebp 4/r32/esp +1429 # . save registers +1430 51/push-ecx +1431 # var curr/ecx : (address primitive) = primitives +1432 8b/-> *(ebp+8) 1/r32/ecx +1433 { +1434 $find-matching-primitive:loop: +1435 # if (curr == null) break +1436 81 7/subop/compare %ecx 0/imm32 +1437 74/jump-if-equal break/disp8 +1438 # if match(curr, stmt) return curr +1439 { +1440 (mu-stmt-matches-primitive? *(ebp+0xc) %ecx) # => eax +1441 3d/compare-eax-and 0/imm32 +1442 74/jump-if-equal break/disp8 +1443 89/<- %eax 1/r32/ecx +1444 eb/jump $find-matching-function:end/disp8 +1445 } +1446 $find-matching-primitive:next-primitive: +1447 # curr = curr->next +1448 8b/-> *(ecx+0x1c) 1/r32/ecx # Primitive-next +1449 eb/jump loop/disp8 +1450 } +1451 # return null +1452 b8/copy-to-eax 0/imm32 +1453 $find-matching-primitive:end: +1454 # . restore registers +1455 59/pop-to-ecx +1456 # . epilogue +1457 89/<- %esp 5/r32/ebp +1458 5d/pop-to-ebp +1459 c3/return +1460 +1461 mu-stmt-matches-function?: # stmt : (address statement), function : (address opcode-info) => result/eax : boolean +1462 # . prologue +1463 55/push-ebp +1464 89/<- %ebp 4/r32/esp +1465 # . save registers +1466 51/push-ecx +1467 # return primitive->name == stmt->operation +1468 8b/-> *(ebp+8) 1/r32/ecx +1469 8b/-> *(ebp+0xc) 0/r32/eax +1470 (string-equal? *(ecx+4) *eax) # Stmt1-operation, Primitive-name => eax +1471 $mu-stmt-matches-function?:end: +1472 # . restore registers +1473 59/pop-to-ecx +1474 # . epilogue +1475 89/<- %esp 5/r32/ebp +1476 5d/pop-to-ebp +1477 c3/return +1478 +1479 mu-stmt-matches-primitive?: # stmt : (address statement), primitive : (address primitive) => result/eax : boolean +1480 # A mu stmt matches a primitive if the name matches, all the inout vars +1481 # match, and all the output vars match. +1482 # Vars match if types match and registers match. +1483 # In addition, a stmt output matches a primitive's output if types match +1484 # and the primitive has a wildcard register. +1485 # . prologue +1486 55/push-ebp +1487 89/<- %ebp 4/r32/esp +1488 # . save registers +1489 51/push-ecx +1490 52/push-edx +1491 53/push-ebx +1492 56/push-esi +1493 57/push-edi +1494 # ecx = stmt +1495 8b/-> *(ebp+8) 1/r32/ecx +1496 # edx = primitive +1497 8b/-> *(ebp+0xc) 2/r32/edx +1498 { +1499 $mu-stmt-matches-primitive?:check-name: +1500 # if (primitive->name != stmt->operation) return false +1501 (string-equal? *(ecx+4) *edx) # Stmt1-operation, Primitive-name => eax +1502 3d/compare-eax-and 0/imm32 +1503 75/jump-if-not-equal break/disp8 +1504 b8/copy-to-eax 0/imm32 +1505 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1506 } +1507 $mu-stmt-matches-primitive?:check-inouts: +1508 # curr = stmt->inouts +1509 8b/-> *(ecx+8) 6/r32/esi # Stmt1-inouts +1510 # curr2 = primitive->inouts +1511 8b/-> *(edx+4) 7/r32/edi # Primitive-inouts +1512 { +1513 # if (curr == 0) return (curr2 == 0) +1514 { +1515 81 7/subop/compare %esi 0/imm32 +1516 75/jump-if-not-equal break/disp8 +1517 { +1518 81 7/subop/compare %edi 0/imm32 +1519 75/jump-if-not-equal break/disp8 +1520 # return true +1521 b8/copy-to-eax 1/imm32 +1522 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1523 } +1524 # return false +1525 b8/copy-to-eax 0/imm32 +1526 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1527 } +1528 # if (curr2 == 0) return false +1529 { +1530 81 7/subop/compare %edi 0/imm32 +1531 75/jump-if-not-equal break/disp8 +1532 b8/copy-to-eax 0/imm32 +1533 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1534 } +1535 # if (curr != curr2) return false +1536 { +1537 (operand-matches-primitive? *esi *edi) # => eax +1538 3d/compare-eax-and 0/imm32 +1539 75/jump-if-not-equal break/disp8 +1540 b8/copy-to-eax 0/imm32 +1541 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1542 } +1543 # curr=curr->next +1544 8b/-> *(ecx+4) 1/r32/ecx # Operand-next +1545 # curr2=curr2->next +1546 8b/-> *(edx+4) 2/r32/edx # Operand-next +1547 } +1548 $mu-stmt-matches-primitive?:check-outputs: +1549 # ecx = stmt +1550 8b/-> *(ebp+8) 1/r32/ecx +1551 # edx = primitive +1552 8b/-> *(ebp+0xc) 2/r32/edx +1553 # curr = stmt->outputs +1554 8b/-> *(ecx+0xc) 6/r32/esi # Stmt1-outputs +1555 # curr2 = primitive->outputs +1556 8b/-> *(edx+8) 7/r32/edi # Primitive-outputs +1557 { +1558 # if (curr == 0) return (curr2 == 0) +1559 { +1560 81 7/subop/compare %esi 0/imm32 +1561 75/jump-if-not-equal break/disp8 +1562 { +1563 81 7/subop/compare %edi 0/imm32 +1564 75/jump-if-not-equal break/disp8 +1565 # return true +1566 b8/copy-to-eax 1/imm32 +1567 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1568 } +1569 # return false +1570 b8/copy-to-eax 0/imm32 +1571 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1572 } +1573 # if (curr2 == 0) return false +1574 { +1575 81 7/subop/compare %edi 0/imm32 +1576 75/jump-if-not-equal break/disp8 +1577 b8/copy-to-eax 0/imm32 +1578 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1579 } +1580 # if (curr != curr2) return false +1581 { +1582 (operand-matches-primitive? *esi *edi) # => eax +1583 3d/compare-eax-and 0/imm32 +1584 75/jump-if-not-equal break/disp8 +1585 b8/copy-to-eax 0/imm32 +1586 e9/jump $mu-stmt-matches-primitive?:end/disp32 +1587 } +1588 # curr=curr->next +1589 8b/-> *(ecx+4) 1/r32/ecx # Operand-next +1590 # curr2=curr2->next +1591 8b/-> *(edx+4) 2/r32/edx # Operand-next +1592 } +1593 $mu-stmt-matches-primitive?:return-true: +1594 b8/copy-to-eax 1/imm32 +1595 $mu-stmt-matches-primitive?:end: +1596 # . restore registers +1597 5f/pop-to-edi +1598 5e/pop-to-esi +1599 5b/pop-to-ebx +1600 5a/pop-to-edx +1601 59/pop-to-ecx +1602 # . epilogue +1603 89/<- %esp 5/r32/ebp +1604 5d/pop-to-ebp +1605 c3/return +1606 +1607 operand-matches-primitive?: # var : (address var), primout-var : (address var) => result/eax : boolean +1608 # . prologue +1609 55/push-ebp +1610 89/<- %ebp 4/r32/esp +1611 # . save registers +1612 56/push-esi +1613 57/push-edi +1614 # esi = var +1615 8b/-> *(ebp+8) 6/r32/esi +1616 # edi = primout-var +1617 8b/-> *(ebp+0xc) 7/r32/edi +1618 # if (var->type != primout-var->type) return false +1619 8b/-> *(esi+4) 0/r32/eax # Var-type +1620 39/compare *(edi+4) 0/r32/eax # Var-type +1621 b8/copy-to-eax 0/imm32/false +1622 75/jump-if-not-equal $operand-matches-primitive?:end/disp8 +1623 # return false if var->register doesn't match primout-var->register +1624 { +1625 # if addresses are equal, don't return here +1626 8b/-> *(esi+0x10) 0/r32/eax +1627 39/compare *(edi+0x10) 0/r32/eax +1628 74/jump-if-equal break/disp8 +1629 # if either address is 0, return false +1630 3d/compare-eax-and 0/imm32 +1631 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result +1632 81 7/subop/compare *(edi+0x10) 0/imm32 +1633 74/jump-if-equal $operand-matches-primitive?:end/disp8 # eax goes from meaning var->register to result +1634 # if primout-var->register is "*", return true +1635 (string-equal? *(edi+0x10) "*") # Var-register +1636 3d/compare-eax-and 0/imm32 +1637 b8/copy-to-eax 1/imm32/true +1638 75/jump-if-not-equal $operand-matches-primitive?:end/disp8 +1639 # if string contents don't match, return false +1640 (string-equal? *(esi+0x10) *(edi+0x10)) # Var-register Var-register +1641 3d/compare-eax-and 0/imm32 +1642 b8/copy-to-eax 0/imm32/false +1643 74/jump-if-equal $operand-matches-primitive?:end/disp8 +1644 } +1645 # return true +1646 b8/copy-to-eax 1/imm32/true +1647 $operand-matches-primitive?:end: +1648 # . restore registers +1649 5f/pop-to-edi +1650 5e/pop-to-esi +1651 # . epilogue +1652 89/<- %esp 5/r32/ebp +1653 5d/pop-to-ebp +1654 c3/return +1655 +1656 test-emit-subx-statement-primitive: +1657 # Primitive operation on a variable on the stack. +1658 # increment foo +1659 # => +1660 # ff 0/subop/increment *(ebp-8) +1661 # +1662 # There's a variable on the var stack as follows: +1663 # name: 'foo' +1664 # type: int +1665 # stack-offset: -8 +1666 # +1667 # There's a primitive with this info: +1668 # name: 'increment' +1669 # inouts: int/mem +1670 # value: 'ff 0/subop/increment' +1671 # +1672 # There's nothing in functions. +1673 # +1674 # . prologue +1675 55/push-ebp +1676 89/<- %ebp 4/r32/esp +1677 # setup +1678 (clear-stream _test-output-stream) +1679 (clear-stream _test-output-buffered-file->buffer) +1680 # var-foo/ecx : var +1681 68/push 0/imm32/no-register +1682 68/push -8/imm32/stack-offset +1683 68/push 1/imm32/block-depth +1684 68/push 1/imm32/type-int +1685 68/push "foo"/imm32 +1686 89/<- %ecx 4/r32/esp +1687 #? $aa-var-in-ecx: +1688 # vars/edx : (stack 1) +1689 51/push-ecx/var-foo +1690 68/push 1/imm32/data-length +1691 68/push 1/imm32/top +1692 89/<- %edx 4/r32/esp +1693 #? $aa-vars-in-edx: +1694 # operand/ebx : (list var) +1695 68/push 0/imm32/next +1696 51/push-ecx/var-foo +1697 89/<- %ebx 4/r32/esp +1698 #? $aa-stmt-operand-in-ebx: +1699 # stmt/esi : statement +1700 68/push 0/imm32/next +1701 68/push 0/imm32/outputs +1702 53/push-ebx/operands +1703 68/push "increment"/imm32/operation +1704 68/push 1/imm32 +1705 89/<- %esi 4/r32/esp +1706 #? $aa-stmt-in-esi: +1707 # primitives/ebx : primitive +1708 68/push 0/imm32/next +1709 68/push 0/imm32/no-imm32 +1710 68/push 0/imm32/no-r32 +1711 68/push 1/imm32/rm32-is-first-inout +1712 68/push "ff 0/subop/increment"/imm32/subx-name +1713 68/push 0/imm32/outputs +1714 53/push-ebx/inouts # hack; in practice we won't have the same var in function definition and call +1715 68/push "increment"/imm32/name +1716 89/<- %ebx 4/r32/esp +1717 $aa-primitive-in-ebx: +1718 # convert +1719 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) +1720 (flush _test-output-buffered-file) +1721 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +1727 # check output +1728 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment *(ebp+0xfffffff8)" "F - test-emit-subx-statement-primitive") +1729 # . epilogue +1730 89/<- %esp 5/r32/ebp +1731 5d/pop-to-ebp +1732 c3/return +1733 +1734 test-emit-subx-statement-primitive-register: +1735 # Primitive operation on a variable in a register. +1736 # foo <- increment +1737 # => +1738 # ff 0/subop/increment %eax # sub-optimal, but should suffice +1739 # +1740 # There's a variable on the var stack as follows: +1741 # name: 'foo' +1742 # type: int +1743 # register: 'eax' +1744 # +1745 # There's a primitive with this info: +1746 # name: 'increment' +1747 # out: int/reg +1748 # value: 'ff 0/subop/increment' +1749 # +1750 # There's nothing in functions. +1751 # +1752 # . prologue +1753 55/push-ebp +1754 89/<- %ebp 4/r32/esp +1755 # setup +1756 (clear-stream _test-output-stream) +1757 (clear-stream _test-output-buffered-file->buffer) +1758 # var-foo/ecx : var in eax +1759 68/push "eax"/imm32/register +1760 68/push 0/imm32/no-stack-offset +1761 68/push 1/imm32/block-depth +1762 68/push 1/imm32/type-int +1763 68/push "foo"/imm32 +1764 89/<- %ecx 4/r32/esp +1765 # vars/edx : (stack 1) +1766 51/push-ecx/var-foo +1767 68/push 1/imm32/data-length +1768 68/push 1/imm32/top +1769 89/<- %edx 4/r32/esp +1770 # operand/ebx : (list var) +1771 68/push 0/imm32/next +1772 51/push-ecx/var-foo +1773 89/<- %ebx 4/r32/esp +1774 # stmt/esi : statement +1775 68/push 0/imm32/next +1776 53/push-ebx/outputs +1777 68/push 0/imm32/inouts +1778 68/push "increment"/imm32/operation +1779 68/push 1/imm32 +1780 89/<- %esi 4/r32/esp +1781 # formal-var/ebx : var in any register +1782 68/push Any-register/imm32 +1783 68/push 0/imm32/no-stack-offset +1784 68/push 1/imm32/block-depth +1785 68/push 1/imm32/type-int +1786 68/push "dummy"/imm32 +1787 89/<- %ebx 4/r32/esp +1788 # operand/ebx : (list var) +1789 68/push 0/imm32/next +1790 53/push-ebx/formal-var +1791 89/<- %ebx 4/r32/esp +1792 # primitives/ebx : primitive +1793 68/push 0/imm32/next +1794 68/push 0/imm32/no-imm32 +1795 68/push 0/imm32/no-r32 +1796 68/push 3/imm32/rm32-in-first-output +1797 68/push "ff 0/subop/increment"/imm32/subx-name +1798 53/push-ebx/outputs +1799 68/push 0/imm32/inouts +1800 68/push "increment"/imm32/name +1801 89/<- %ebx 4/r32/esp +1802 # convert +1803 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) +1804 (flush _test-output-buffered-file) +1805 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +1811 # check output +1812 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register") +1813 # . epilogue +1814 89/<- %esp 5/r32/ebp +1815 5d/pop-to-ebp +1816 c3/return +1817 +1818 test-emit-subx-statement-select-primitive: +1819 # Select the right primitive between overloads. +1820 # foo <- increment +1821 # => +1822 # ff 0/subop/increment %eax # sub-optimal, but should suffice +1823 # +1824 # There's a variable on the var stack as follows: +1825 # name: 'foo' +1826 # type: int +1827 # register: 'eax' +1828 # +1829 # There's two primitives, as follows: +1830 # - name: 'increment' +1831 # out: int/reg +1832 # value: 'ff 0/subop/increment' +1833 # - name: 'increment' +1834 # inout: int/mem +1835 # value: 'ff 0/subop/increment' +1836 # +1837 # There's nothing in functions. +1838 # +1839 # . prologue +1840 55/push-ebp +1841 89/<- %ebp 4/r32/esp +1842 # setup +1843 (clear-stream _test-output-stream) +1844 (clear-stream _test-output-buffered-file->buffer) +1845 # var-foo/ecx : var in eax +1846 68/push "eax"/imm32/register +1847 68/push 0/imm32/no-stack-offset +1848 68/push 1/imm32/block-depth +1849 68/push 1/imm32/type-int +1850 68/push "foo"/imm32 +1851 89/<- %ecx 4/r32/esp +1852 # vars/edx : (stack 1) +1853 51/push-ecx/var-foo +1854 68/push 1/imm32/data-length +1855 68/push 1/imm32/top +1856 89/<- %edx 4/r32/esp +1857 # real-outputs/edi : (list var) +1858 68/push 0/imm32/next +1859 51/push-ecx/var-foo +1860 89/<- %edi 4/r32/esp +1861 # stmt/esi : statement +1862 68/push 0/imm32/next +1863 57/push-edi/outputs +1864 68/push 0/imm32/inouts +1865 68/push "increment"/imm32/operation +1866 68/push 1/imm32 +1867 89/<- %esi 4/r32/esp +1868 # formal-var/ebx : var in any register +1869 68/push Any-register/imm32 +1870 68/push 0/imm32/no-stack-offset +1871 68/push 1/imm32/block-depth +1872 68/push 1/imm32/type-int +1873 68/push "dummy"/imm32 +1874 89/<- %ebx 4/r32/esp +1875 # formal-outputs/ebx : (list var) +1876 68/push 0/imm32/next +1877 53/push-ebx/formal-var +1878 89/<- %ebx 4/r32/esp +1879 # primitive1/ebx : primitive +1880 68/push 0/imm32/next +1881 68/push 0/imm32/no-imm32 +1882 68/push 0/imm32/no-r32 +1883 68/push 3/imm32/rm32-in-first-output +1884 68/push "ff 0/subop/increment"/imm32/subx-name +1885 53/push-ebx/outputs/formal-outputs +1886 68/push 0/imm32/inouts +1887 68/push "increment"/imm32/name +1888 89/<- %ebx 4/r32/esp +1889 # primitives/ebx : primitive +1890 53/push-ebx/next +1891 68/push 0/imm32/no-imm32 +1892 68/push 0/imm32/no-r32 +1893 68/push 1/imm32/rm32-is-first-inout +1894 68/push "ff 0/subop/increment"/imm32/subx-name +1895 68/push 0/imm32/outputs +1896 57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call +1897 68/push "increment"/imm32/name +1898 89/<- %ebx 4/r32/esp +1899 # convert +1900 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) +1901 (flush _test-output-buffered-file) +1902 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +1908 # check output +1909 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive") +1910 # . epilogue +1911 89/<- %esp 5/r32/ebp +1912 5d/pop-to-ebp +1913 c3/return +1914 +1915 test-emit-subx-statement-select-primitive-2: +1916 # Select the right primitive between overloads. +1917 # foo <- increment +1918 # => +1919 # ff 0/subop/increment %eax # sub-optimal, but should suffice +1920 # +1921 # There's a variable on the var stack as follows: +1922 # name: 'foo' +1923 # type: int +1924 # register: 'eax' +1925 # +1926 # There's two primitives, as follows: +1927 # - name: 'increment' +1928 # out: int/reg +1929 # value: 'ff 0/subop/increment' +1930 # - name: 'increment' +1931 # inout: int/mem +1932 # value: 'ff 0/subop/increment' +1933 # +1934 # There's nothing in functions. +1935 # +1936 # . prologue +1937 55/push-ebp +1938 89/<- %ebp 4/r32/esp +1939 # setup +1940 (clear-stream _test-output-stream) +1941 (clear-stream _test-output-buffered-file->buffer) +1942 # var-foo/ecx : var in eax +1943 68/push "eax"/imm32/register +1944 68/push 0/imm32/no-stack-offset +1945 68/push 1/imm32/block-depth +1946 68/push 1/imm32/type-int +1947 68/push "foo"/imm32 +1948 89/<- %ecx 4/r32/esp +1949 # vars/edx : (stack 1) +1950 51/push-ecx/var-foo +1951 68/push 1/imm32/data-length +1952 68/push 1/imm32/top +1953 89/<- %edx 4/r32/esp +1954 # inouts/edi : (list var) +1955 68/push 0/imm32/next +1956 51/push-ecx/var-foo +1957 89/<- %edi 4/r32/esp +1958 # stmt/esi : statement +1959 68/push 0/imm32/next +1960 68/push 0/imm32/outputs +1961 57/push-edi/inouts +1962 68/push "increment"/imm32/operation +1963 68/push 1/imm32 +1964 89/<- %esi 4/r32/esp +1965 # formal-var/ebx : var in any register +1966 68/push Any-register/imm32 +1967 68/push 0/imm32/no-stack-offset +1968 68/push 1/imm32/block-depth +1969 68/push 1/imm32/type-int +1970 68/push "dummy"/imm32 +1971 89/<- %ebx 4/r32/esp +1972 # operand/ebx : (list var) +1973 68/push 0/imm32/next +1974 53/push-ebx/formal-var +1975 89/<- %ebx 4/r32/esp +1976 # primitive1/ebx : primitive +1977 68/push 0/imm32/next +1978 68/push 0/imm32/no-imm32 +1979 68/push 0/imm32/no-r32 +1980 68/push 3/imm32/rm32-in-first-output +1981 68/push "ff 0/subop/increment"/imm32/subx-name +1982 53/push-ebx/outputs/formal-outputs +1983 68/push 0/imm32/inouts +1984 68/push "increment"/imm32/name +1985 89/<- %ebx 4/r32/esp +1986 # primitives/ebx : primitive +1987 53/push-ebx/next +1988 68/push 0/imm32/no-imm32 +1989 68/push 0/imm32/no-r32 +1990 68/push 1/imm32/rm32-is-first-inout +1991 68/push "ff 0/subop/increment"/imm32/subx-name +1992 68/push 0/imm32/outputs +1993 57/push-edi/inouts/real-outputs # hack; in practice we won't have the same var in function definition and call +1994 68/push "increment"/imm32/name +1995 89/<- %ebx 4/r32/esp +1996 # convert +1997 (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) +1998 (flush _test-output-buffered-file) +1999 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +2005 # check output +2006 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-select-primitive-2") +2007 # . epilogue +2008 89/<- %esp 5/r32/ebp +2009 5d/pop-to-ebp +2010 c3/return +2011 +2012 test-increment-register: +2013 # Select the right primitive between overloads. +2014 # foo <- increment +2015 # => +2016 # ff 0/subop/increment %eax # sub-optimal, but should suffice +2017 # +2018 # There's a variable on the var stack as follows: +2019 # name: 'foo' +2020 # type: int +2021 # register: 'eax' +2022 # +2023 # Primitives are the global definitions. +2024 # +2025 # There are no functions defined. +2026 # +2027 # . prologue +2028 55/push-ebp +2029 89/<- %ebp 4/r32/esp +2030 # setup +2031 (clear-stream _test-output-stream) +2032 (clear-stream _test-output-buffered-file->buffer) +2033 # var-foo/ecx : var in eax +2034 68/push "eax"/imm32/register +2035 68/push 0/imm32/no-stack-offset +2036 68/push 1/imm32/block-depth +2037 68/push 1/imm32/type-int +2038 68/push "foo"/imm32 +2039 89/<- %ecx 4/r32/esp +2040 # vars/edx : (stack 1) +2041 51/push-ecx/var-foo +2042 68/push 1/imm32/data-length +2043 68/push 1/imm32/top +2044 89/<- %edx 4/r32/esp +2045 # real-outputs/edi : (list var) +2046 68/push 0/imm32/next +2047 51/push-ecx/var-foo +2048 89/<- %edi 4/r32/esp +2049 # stmt/esi : statement +2050 68/push 0/imm32/next +2051 57/push-edi/outputs +2052 68/push 0/imm32/inouts +2053 68/push "increment"/imm32/operation +2054 68/push 1/imm32 +2055 89/<- %esi 4/r32/esp +2056 # convert +2057 (emit-subx-statement _test-output-buffered-file %esi %edx Primitives 0) +2058 (flush _test-output-buffered-file) +2059 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +2065 # check output +2066 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-increment-register") +2067 # . epilogue +2068 89/<- %esp 5/r32/ebp +2069 5d/pop-to-ebp +2070 c3/return +2071 +2072 test-increment-var: +2073 # Select the right primitive between overloads. +2074 # foo <- increment +2075 # => +2076 # ff 0/subop/increment %eax # sub-optimal, but should suffice +2077 # +2078 # There's a variable on the var stack as follows: +2079 # name: 'foo' +2080 # type: int +2081 # register: 'eax' +2082 # +2083 # Primitives are the global definitions. +2084 # +2085 # There are no functions defined. +2086 # +2087 # . prologue +2088 55/push-ebp +2089 89/<- %ebp 4/r32/esp +2090 # setup +2091 (clear-stream _test-output-stream) +2092 (clear-stream _test-output-buffered-file->buffer) +2093 # var-foo/ecx : var in eax +2094 68/push "eax"/imm32/register +2095 68/push 0/imm32/no-stack-offset +2096 68/push 1/imm32/block-depth +2097 68/push 1/imm32/type-int +2098 68/push "foo"/imm32 +2099 89/<- %ecx 4/r32/esp +2100 # vars/edx : (stack 1) +2101 51/push-ecx/var-foo +2102 68/push 1/imm32/data-length +2103 68/push 1/imm32/top +2104 89/<- %edx 4/r32/esp +2105 # inouts/edi : (list var) +2106 68/push 0/imm32/next +2107 51/push-ecx/var-foo +2108 89/<- %edi 4/r32/esp +2109 # stmt/esi : statement +2110 68/push 0/imm32/next +2111 68/push 0/imm32/outputs +2112 57/push-edi/inouts +2113 68/push "increment"/imm32/operation +2114 68/push 1/imm32 +2115 89/<- %esi 4/r32/esp +2116 # convert +2117 (emit-subx-statement _test-output-buffered-file %esi %edx Primitives 0) +2118 (flush _test-output-buffered-file) +2119 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +2125 # check output +2126 (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-increment-var") +2127 # . epilogue +2128 89/<- %esp 5/r32/ebp +2129 5d/pop-to-ebp +2130 c3/return +2131 +2132 test-add-reg-to-reg: +2133 # var1/reg <- add var2/reg +2134 # => +2135 # 01 %var1 var2 +2136 # +2137 # . prologue +2138 55/push-ebp +2139 89/<- %ebp 4/r32/esp +2140 # setup +2141 (clear-stream _test-output-stream) +2142 (clear-stream _test-output-buffered-file->buffer) +2143 # var-var1/ecx : var in eax +2144 68/push "eax"/imm32/register +2145 68/push 0/imm32/no-stack-offset +2146 68/push 1/imm32/block-depth +2147 68/push 1/imm32/type-int +2148 68/push "var1"/imm32 +2149 89/<- %ecx 4/r32/esp +2150 # var-var2/edx : var in ecx +2151 68/push "ecx"/imm32/register +2152 68/push 0/imm32/no-stack-offset +2153 68/push 1/imm32/block-depth +2154 68/push 1/imm32/type-int +2155 68/push "var2"/imm32 +2156 89/<- %edx 4/r32/esp +2157 # inouts/esi : (list var2) +2158 68/push 0/imm32/next +2159 52/push-edx/var-var2 +2160 89/<- %esi 4/r32/esp +2161 # outputs/edi : (list var1) +2162 68/push 0/imm32/next +2163 51/push-ecx/var-var1 +2164 89/<- %edi 4/r32/esp +2165 # stmt/esi : statement +2166 68/push 0/imm32/next +2167 57/push-edi/outputs +2168 56/push-esi/inouts +2169 68/push "add"/imm32/operation +2170 68/push 1/imm32 +2171 89/<- %esi 4/r32/esp 2172 # convert -2173 (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx) +2173 (emit-subx-statement _test-output-buffered-file %esi 0 Primitives 0) 2174 (flush _test-output-buffered-file) -2175 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- +2175 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 2181 # check output -2182 (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call") +2182 (check-next-stream-line-equal _test-output-stream "01 %eax 0x00000001/r32" "F - test-add-reg-to-reg") 2183 # . epilogue 2184 89/<- %esp 5/r32/ebp 2185 5d/pop-to-ebp 2186 c3/return 2187 -2188 test-emit-subx-statement-function-call-with-literal-arg: -2189 # Call a function on a literal. -2190 # f 34 -2191 # => -2192 # (f2 34) -2193 # -2194 # . prologue -2195 55/push-ebp -2196 89/<- %ebp 4/r32/esp -2197 # setup -2198 (clear-stream _test-output-stream) -2199 (clear-stream _test-output-buffered-file->buffer) -2200 # var-foo/ecx : literal -2201 68/push 0/imm32/no-register -2202 68/push 0/imm32/no-stack-offset -2203 68/push 0/imm32/block-depth -2204 68/push 0/imm32/type-literal -2205 68/push "34"/imm32 -2206 89/<- %ecx 4/r32/esp -2207 # vars/edx = (stack 1) -2208 51/push-ecx/var-foo -2209 68/push 1/imm32/data-length -2210 68/push 1/imm32/top -2211 89/<- %edx 4/r32/esp -2212 # operands/esi : (list var) -2213 68/push 0/imm32/next -2214 51/push-ecx/var-foo -2215 89/<- %esi 4/r32/esp -2216 # stmt/esi : statement -2217 68/push 0/imm32/next -2218 68/push 0/imm32/outputs -2219 56/push-esi/inouts -2220 68/push "f"/imm32/operation -2221 89/<- %esi 4/r32/esp -2222 # functions/ebx : function -2223 68/push 0/imm32/next -2224 68/push 0/imm32/body -2225 68/push 0/imm32/outputs -2226 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call -2227 68/push "f2"/imm32/subx-name -2228 68/push "f"/imm32/name -2229 89/<- %ebx 4/r32/esp -2230 # convert -2231 (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx) -2232 (flush _test-output-buffered-file) -2233 +-- 6 lines: #? # dump _test-output-stream -------------------------------------------------------------------------------------------------------------- -2239 # check output -2240 (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-statement-function-call-with-literal-arg") -2241 # . epilogue -2242 89/<- %esp 5/r32/ebp -2243 5d/pop-to-ebp -2244 c3/return -2245 -2246 emit-subx-prologue: # out : (address buffered-file) -2247 # . prologue -2248 55/push-ebp -2249 89/<- %ebp 4/r32/esp -2250 # -2251 (write-buffered *(ebp+8) "# . prologue\n") -2252 (write-buffered *(ebp+8) "55/push-ebp\n") -2253 (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n") -2254 $emit-subx-prologue:end: -2255 # . epilogue -2256 89/<- %esp 5/r32/ebp -2257 5d/pop-to-ebp -2258 c3/return -2259 -2260 emit-subx-epilogue: # out : (address buffered-file) -2261 # . prologue -2262 55/push-ebp -2263 89/<- %ebp 4/r32/esp -2264 # -2265 (write-buffered *(ebp+8) "# . epilogue\n") -2266 (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n") -2267 (write-buffered *(ebp+8) "5d/pop-to-ebp\n") -2268 (write-buffered *(ebp+8) "c3/return\n") -2269 $emit-subx-epilogue:end: -2270 # . epilogue -2271 89/<- %esp 5/r32/ebp -2272 5d/pop-to-ebp -2273 c3/return +2188 test-add-literal-to-reg: +2189 # var1/eax <- add 0x34 +2190 # => +2191 # 81 0/subop/add %eax 0x34/imm32 +2192 # +2193 # . prologue +2194 55/push-ebp +2195 89/<- %ebp 4/r32/esp +2196 # setup +2197 (clear-stream _test-output-stream) +2198 (clear-stream _test-output-buffered-file->buffer) +2199 # var-var1/ecx : var in eax +2200 68/push "eax"/imm32/register +2201 68/push 0/imm32/no-stack-offset +2202 68/push 1/imm32/block-depth +2203 68/push 1/imm32/type-int +2204 68/push "var1"/imm32 +2205 89/<- %ecx 4/r32/esp +2206 # var-var2/edx : var literal +2207 68/push 0/imm32/no-register +2208 68/push 0/imm32/no-stack-offset +2209 68/push 1/imm32/block-depth +2210 68/push 0/imm32/type-literal +2211 68/push "0x34"/imm32 +2212 89/<- %edx 4/r32/esp +2213 # inouts/esi : (list var2) +2214 68/push 0/imm32/next +2215 52/push-edx/var-var2 +2216 89/<- %esi 4/r32/esp +2217 # outputs/edi : (list var1) +2218 68/push 0/imm32/next +2219 51/push-ecx/var-var1 +2220 89/<- %edi 4/r32/esp +2221 # stmt/esi : statement +2222 68/push 0/imm32/next +2223 57/push-edi/outputs +2224 56/push-esi/inouts +2225 68/push "add"/imm32/operation +2226 68/push 1/imm32 +2227 89/<- %esi 4/r32/esp +2228 # convert +2229 (emit-subx-statement _test-output-buffered-file %esi 0 Primitives 0) +2230 (flush _test-output-buffered-file) +2231 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +2237 # check output +2238 (check-next-stream-line-equal _test-output-stream "81 0/subop/add %eax 0x34/imm32" "F - test-add-literal-to-reg") +2239 # . epilogue +2240 89/<- %esp 5/r32/ebp +2241 5d/pop-to-ebp +2242 c3/return +2243 +2244 test-emit-subx-statement-function-call: +2245 # Call a function on a variable on the stack. +2246 # f foo +2247 # => +2248 # (f2 *(ebp-8)) +2249 # (Changing the function name supports overloading in general, but here it +2250 # just serves to help disambiguate things.) +2251 # +2252 # There's a variable on the var stack as follows: +2253 # name: 'foo' +2254 # type: int +2255 # stack-offset: -8 +2256 # +2257 # There's nothing in primitives. +2258 # +2259 # There's a function with this info: +2260 # name: 'f' +2261 # inout: int/mem +2262 # value: 'f2' +2263 # +2264 # . prologue +2265 55/push-ebp +2266 89/<- %ebp 4/r32/esp +2267 # setup +2268 (clear-stream _test-output-stream) +2269 (clear-stream _test-output-buffered-file->buffer) +2270 # var-foo/ecx : var +2271 68/push 0/imm32/no-register +2272 68/push -8/imm32/stack-offset +2273 68/push 0/imm32/block-depth +2274 68/push 1/imm32/type-int +2275 68/push "foo"/imm32 +2276 89/<- %ecx 4/r32/esp +2277 # vars/edx = (stack 1) +2278 51/push-ecx/var-foo +2279 68/push 1/imm32/data-length +2280 68/push 1/imm32/top +2281 89/<- %edx 4/r32/esp +2282 # operands/esi : (list var) +2283 68/push 0/imm32/next +2284 51/push-ecx/var-foo +2285 89/<- %esi 4/r32/esp +2286 # stmt/esi : statement +2287 68/push 0/imm32/next +2288 68/push 0/imm32/outputs +2289 56/push-esi/inouts +2290 68/push "f"/imm32/operation +2291 68/push 1/imm32 +2292 89/<- %esi 4/r32/esp +2293 # functions/ebx : function +2294 68/push 0/imm32/next +2295 68/push 0/imm32/body +2296 68/push 0/imm32/outputs +2297 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call +2298 68/push "f2"/imm32/subx-name +2299 68/push "f"/imm32/name +2300 89/<- %ebx 4/r32/esp +2301 # convert +2302 (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx) +2303 (flush _test-output-buffered-file) +2304 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +2310 # check output +2311 (check-next-stream-line-equal _test-output-stream "(f2 *(ebp+0xfffffff8))" "F - test-emit-subx-statement-function-call") +2312 # . epilogue +2313 89/<- %esp 5/r32/ebp +2314 5d/pop-to-ebp +2315 c3/return +2316 +2317 test-emit-subx-statement-function-call-with-literal-arg: +2318 # Call a function on a literal. +2319 # f 34 +2320 # => +2321 # (f2 34) +2322 # +2323 # . prologue +2324 55/push-ebp +2325 89/<- %ebp 4/r32/esp +2326 # setup +2327 (clear-stream _test-output-stream) +2328 (clear-stream _test-output-buffered-file->buffer) +2329 # var-foo/ecx : literal +2330 68/push 0/imm32/no-register +2331 68/push 0/imm32/no-stack-offset +2332 68/push 0/imm32/block-depth +2333 68/push 0/imm32/type-literal +2334 68/push "34"/imm32 +2335 89/<- %ecx 4/r32/esp +2336 # vars/edx = (stack 1) +2337 51/push-ecx/var-foo +2338 68/push 1/imm32/data-length +2339 68/push 1/imm32/top +2340 89/<- %edx 4/r32/esp +2341 # operands/esi : (list var) +2342 68/push 0/imm32/next +2343 51/push-ecx/var-foo +2344 89/<- %esi 4/r32/esp +2345 # stmt/esi : statement +2346 68/push 0/imm32/next +2347 68/push 0/imm32/outputs +2348 56/push-esi/inouts +2349 68/push "f"/imm32/operation +2350 68/push 1/imm32 +2351 89/<- %esi 4/r32/esp +2352 # functions/ebx : function +2353 68/push 0/imm32/next +2354 68/push 0/imm32/body +2355 68/push 0/imm32/outputs +2356 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call +2357 68/push "f2"/imm32/subx-name +2358 68/push "f"/imm32/name +2359 89/<- %ebx 4/r32/esp +2360 # convert +2361 (emit-subx-statement _test-output-buffered-file %esi %edx 0 %ebx) +2362 (flush _test-output-buffered-file) +2363 +-- 6 lines: #? # dump _test-output-stream ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +2369 # check output +2370 (check-next-stream-line-equal _test-output-stream "(f2 34)" "F - test-emit-subx-statement-function-call-with-literal-arg") +2371 # . epilogue +2372 89/<- %esp 5/r32/ebp +2373 5d/pop-to-ebp +2374 c3/return +2375 +2376 emit-subx-prologue: # out : (address buffered-file) +2377 # . prologue +2378 55/push-ebp +2379 89/<- %ebp 4/r32/esp +2380 # +2381 (write-buffered *(ebp+8) "# . prologue\n") +2382 (write-buffered *(ebp+8) "55/push-ebp\n") +2383 (write-buffered *(ebp+8) "89/<- %ebp 4/r32/esp\n") +2384 $emit-subx-prologue:end: +2385 # . epilogue +2386 89/<- %esp 5/r32/ebp +2387 5d/pop-to-ebp +2388 c3/return +2389 +2390 emit-subx-epilogue: # out : (address buffered-file) +2391 # . prologue +2392 55/push-ebp +2393 89/<- %ebp 4/r32/esp +2394 # +2395 (write-buffered *(ebp+8) "# . epilogue\n") +2396 (write-buffered *(ebp+8) "89/<- %esp 5/r32/ebp\n") +2397 (write-buffered *(ebp+8) "5d/pop-to-ebp\n") +2398 (write-buffered *(ebp+8) "c3/return\n") +2399 $emit-subx-epilogue:end: +2400 # . epilogue +2401 89/<- %esp 5/r32/ebp +2402 5d/pop-to-ebp +2403 c3/return diff --git a/test_apps b/test_apps index e9be6d92..8ea9b562 100755 --- a/test_apps +++ b/test_apps @@ -315,7 +315,7 @@ done # Mu translator echo mu.subx -./ntranslate init.$OS 0*.subx apps/subx-params.subx apps/mu.subx +./ntranslate init.$OS 0*.subx apps/mu.subx mv a.elf apps/mu [ "$1" != record ] && git diff --exit-code apps/mu apps/mu test -- cgit 1.4.1-2-gfad0