From de3aecff25e2da4eef2d30f4775c37be2eaa2899 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Fri, 15 Nov 2019 18:41:45 -0800 Subject: 5746 After much struggle, one more test: emitting a primitive with a register operand. The following two instructions have the same name: reg <- increment increment var and they emit the same opcodes: ff 0/subop But they're considered distinct policies in the code-generation 'table', one for incrementing variables on the stack and the other for incrementing variables in a register. --- apps/mu | Bin 47961 -> 48867 bytes apps/mu.subx | 373 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 336 insertions(+), 37 deletions(-) (limited to 'apps') diff --git a/apps/mu b/apps/mu index 910175ec..9cd19eb8 100755 Binary files a/apps/mu and b/apps/mu differ diff --git a/apps/mu.subx b/apps/mu.subx index 2e5d93ab..ddac7bb8 100644 --- a/apps/mu.subx +++ b/apps/mu.subx @@ -182,8 +182,14 @@ # mu-inouts: linked list of vars to check # mu-outputs: linked list of vars to check # subx-name: string -# subx-rm32: enum of 2 states -# subx-r32: enum of 3 states +# subx-rm32: enum arg-location +# subx-r32: enum arg-location +# subx-imm32: enum arg-location +# arg-location: enum +# 0 means none +# 1 means first inout +# 2 means second inout +# 3 means first output # == Translating a block # Emit block name if necessary @@ -224,15 +230,21 @@ Function-size: Primitive-name: 0/imm32 Primitive-inouts: # (address list var) - 8/imm32 + 4/imm32 Primitive-outputs: # (address list var) + 8/imm32 +Primitive-subx-name: # (address string) 0xc/imm32 -Primitive-subx-name: - 4/imm32 -Primitive-next: # (address function) +Primitive-subx-rm32: # enum arg-location + 0x10/imm32 +Primitive-subx-r32: # enum arg-location 0x14/imm32 +Primitive-subx-imm32: # enum arg-location + 0x18/imm32 +Primitive-next: # (address function) + 0x1c/imm32 Primitive-size: - 0x18/imm32/24 + 0x20/imm32/24 Stmt-operation: 0/imm32 @@ -253,11 +265,17 @@ Var-block: 8/imm32 Var-stack-offset: 0xc/imm32 -Var-register-index: +Var-register: 0x10/imm32 Var-size: 0x14/imm32 +Any-register: # "*" + # size + 1/imm32 + # data + 2a/asterisk + == code Entry: @@ -876,7 +894,7 @@ $emit-subx-block:end: 5d/pop-to-ebp c3/return -emit-subx-statement: # out : (address buffered-file), stmt : (address statement), vars : (stack var), primitives : (address opcode-info), functions : (address function) +emit-subx-statement: # out : (address buffered-file), stmt : (address statement), vars : (stack var), primitives : (address primitive), functions : (address function) # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp @@ -885,18 +903,20 @@ emit-subx-statement: # out : (address buffered-file), stmt : (address statement 51/push-ecx # if stmt matches a primitive, emit it { - (find-matching-function *(ebp+0x14) *(ebp+0xc)) +$emit-subx-statement:primitive: + (find-matching-primitive *(ebp+0x14) *(ebp+0xc)) # primitives, stmt => curr/eax 3d/compare-eax-and 0/imm32 74/jump-if-equal break/disp8 - (emit-subx-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) + (emit-subx-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) # out, stmt, vars, curr e9/jump $emit-subx-statement:end/disp32 } # else if stmt matches a function, emit a call to it { - (find-matching-function *(ebp+0x18) *(ebp+0xc)) +$emit-subx-statement:call: + (find-matching-function *(ebp+0x18) *(ebp+0xc)) # functions, stmt => curr/eax 3d/compare-eax-and 0/imm32 74/jump-if-equal break/disp8 - (emit-subx-call *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) + (emit-subx-call *(ebp+8) *(ebp+0xc) *(ebp+0x10) %eax) # out, stmt, vars, curr e9/jump $emit-subx-statement:end/disp32 } # else abort @@ -929,23 +949,117 @@ emit-subx-primitive: # out : (address buffered-file), stmt : (address statement # . save registers 50/push-eax 51/push-ecx - # - emit primitive name + # ecx = primitive 8b/-> *(ebp+0x14) 1/r32/ecx - (write-buffered *(ebp+8) *(ecx+4)) # Function-subx-name - # - emit arguments - # var args/ecx : (list var) = stmt->inouts - 8b/-> *(ebp+0xc) 1/r32/ecx - 8b/-> *(ecx+4) 1/r32/ecx # Stmt-inouts + # emit primitive name + (write-buffered *(ebp+8) *(ecx+0xc)) # Primitive-subx-name + # emit rm32 if necessary + (emit-subx-rm32 *(ebp+8) *(ecx+0x10) *(ebp+0xc)) # out, Primitive-subx-rm32, stmt +#? # emit r32 if necessary +#? (emit-subx-r32 *(ebp+8) *(ecx+0x14) *(ebp+0xc)) # out, Primitive-subx-r32, stmt +#? # emit imm32 if necessary +#? (emit-subx-imm32 *(ebp+8) *(ecx+0x18) *(ebp+0xc)) # out, Primitive-subx-imm32, stmt +$emit-subx-primitive:end: + # . restore registers + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +emit-subx-rm32: # out : (address buffered-file), l : arg-location, stmt : (address statement) + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 50/push-eax + # if (l == 0) return + 81 7/subop/compare *(ebp+0xc) 0/imm32 + 74/jump-if-equal $emit-subx-rm32:end/disp8 + # + (get-stmt-operand-from-arg-location *(ebp+0x10) *(ebp+0xc)) # stmt, l => var/eax + (emit-subx-call-operand *(ebp+8) %eax) # out, var +$emit-subx-rm32:end: + # . restore registers + 58/pop-to-eax + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +get-stmt-operand-from-arg-location: # stmt : (address statement), l : arg-location -> var/eax : (address variable) + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 51/push-ecx + # eax = l + 8b/-> *(ebp+0xc) 0/r32/eax + # ecx = stmt + 8b/-> *(ebp+8) 1/r32/ecx + # if (l == 1) return stmt->inouts->var { - # if (curr == null) break - 81 7/subop/compare %ecx 0/imm32 - 74/jump-if-equal break/disp8 - # - (emit-subx-call-operand *(ebp+8) *ecx) - # args = args->next - 8b/-> *(ecx+4) 1/r32/ecx + 3d/compare-eax-and 1/imm32 + 75/jump-if-not-equal break/disp8 +$get-stmt-operand-from-arg-location:1: + 8b/-> *(ecx+4) 0/r32/eax # Stmt-inouts + 8b/-> *eax 0/r32/eax # Operand-var + eb/jump $get-stmt-operand-from-arg-location:end/disp8 } -$emit-subx-primitive:end: + # if (l == 2) return stmt->inouts->next->var + { + 3d/compare-eax-and 2/imm32 + 75/jump-if-not-equal break/disp8 +$get-stmt-operand-from-arg-location:2: + 8b/-> *(ecx+4) 0/r32/eax # Stmt-inouts + 8b/-> *(eax+4) 0/r32/eax # Operand-next + 8b/-> *eax 0/r32/eax # Operand-var + eb/jump $get-stmt-operand-from-arg-location:end/disp8 + } + # if (l == 3) return stmt->outputs + { + 3d/compare-eax-and 3/imm32 + 75/jump-if-not-equal break/disp8 +$get-stmt-operand-from-arg-location:3: + 8b/-> *(ecx+8) 0/r32/eax # Stmt-outputs + 8b/-> *eax 0/r32/eax # Operand-var + eb/jump $get-stmt-operand-from-arg-location:end/disp8 + } + # abort + e9/jump $get-stmt-operand-from-arg-location:abort/disp32 +$get-stmt-operand-from-arg-location:end: + # . restore registers + 59/pop-to-ecx + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +$get-stmt-operand-from-arg-location:abort: + # error("invalid arg-location " eax) + (write-buffered Stderr "invalid arg-location ") + (print-int32-buffered Stderr %eax) + (write-buffered Stderr "\n") + (flush Stderr) + # . syscall(exit, 1) + bb/copy-to-ebx 1/imm32 + b8/copy-to-eax 1/imm32/exit + cd/syscall 0x80/imm8 + # never gets here + +emit-subx-r32: # out : (address buffered-file), l : arg-location, stmt : (address statement) + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 50/push-eax + 51/push-ecx + # location/ecx : enum = primitive->subx-r32 + # if (location == 0) return + # var/ecx : var = get-operand(stmt, primitive->subx-rm32) + # emit-subx-call-operand(out, var) +$emit-subx-r32:end: # . restore registers 59/pop-to-ecx 58/pop-to-eax @@ -954,6 +1068,37 @@ $emit-subx-primitive:end: 5d/pop-to-ebp c3/return +emit-subx-imm32: # out : (address buffered-file), l : arg-location, stmt : (address statement) + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 50/push-eax + 51/push-ecx + # var/ecx : var = get-operand(stmt, primitive->subx-rm32) + # emit-subx-call-operand(out, var) +$emit-subx-imm32:end: + # . restore registers + 59/pop-to-ecx + 58/pop-to-eax + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +#? # var args/ecx : (list var) = stmt->inouts +#? 8b/-> *(ebp+0xc) 1/r32/ecx +#? 8b/-> *(ecx+4) 1/r32/ecx # Stmt-inouts +#? { +#? # if (curr == null) break +#? 81 7/subop/compare %ecx 0/imm32 +#? 74/jump-if-equal break/disp8 +#? # +#? (emit-subx-call-operand *(ebp+8) *ecx) +#? # args = args->next +#? 8b/-> *(ecx+4) 1/r32/ecx +#? } + emit-subx-call: # out : (address buffered-file), stmt : (address statement), vars : (address variable), callee : (address function) # . prologue 55/push-ebp @@ -996,12 +1141,27 @@ emit-subx-call-operand: # out : (address buffered-file), operand : (address var 89/<- %ebp 4/r32/esp # . save registers 50/push-eax - # - (write-buffered *(ebp+8) Space) - (write-buffered *(ebp+8) "*(ebp+") + # eax = operand 8b/-> *(ebp+0xc) 0/r32/eax - (print-int32-buffered *(ebp+8) *(eax+0xc)) # Var-stack-offset - (write-buffered *(ebp+8) ")") + # if (operand->register) emit "%__" + { + 81 7/subop/compare *(eax+0x10) 0/imm32 # Var-register + 74/jump-if-equal break/disp8 +$emit-subx-call-operand:register: + (write-buffered *(ebp+8) " %") + (write-buffered *(ebp+8) *(eax+0x10)) # Var-register + } + # else if (operand->stack-offset) emit "*(ebp+__)" + { + 81 7/subop/compare *(eax+0xc) 0/imm32 # Var-stack-offset + 74/jump-if-equal break/disp8 +$emit-subx-call-operand:stack: + (write-buffered *(ebp+8) Space) + (write-buffered *(ebp+8) "*(ebp+") + 8b/-> *(ebp+0xc) 0/r32/eax + (print-int32-buffered *(ebp+8) *(eax+0xc)) # Var-stack-offset + (write-buffered *(ebp+8) ")") + } $emit-subx-call-operand:end: # . restore registers 58/pop-to-eax @@ -1031,7 +1191,7 @@ find-matching-function: # functions : (address function), stmt : (address state eb/jump $find-matching-function:end/disp8 } # curr = curr->next - 8b/-> *(ecx+0x10) 1/r32/ecx + 8b/-> *(ecx+0x10) 1/r32/ecx # Function-next eb/jump loop/disp8 } # return null @@ -1044,7 +1204,41 @@ $find-matching-function:end: 5d/pop-to-ebp c3/return -mu-stmt-matches-function?: # stmt : (address statement), primitive : (address opcode-info) => result/eax : boolean +find-matching-primitive: # primitives : (address primitive), stmt : (address statement) -> result/eax : (address primitive) + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 51/push-ecx + # var curr/ecx : (address primitive) = primitives + 8b/-> *(ebp+8) 1/r32/ecx + { + # if (curr == null) break + 81 7/subop/compare %ecx 0/imm32 + 74/jump-if-equal break/disp8 + # if match(curr, stmt) return curr + { + (mu-stmt-matches-primitive? *(ebp+0xc) %ecx) # => eax + 3d/compare-eax-and 0/imm32 + 74/jump-if-equal break/disp8 + 89/<- %eax 1/r32/ecx + eb/jump $find-matching-function:end/disp8 + } + # curr = curr->next + 8b/-> *(ecx+0x18) 1/r32/ecx # Primitive-next + eb/jump loop/disp8 + } + # return null + b8/copy-to-eax 0/imm32 +$find-matching-primitive:end: + # . restore registers + 59/pop-to-ecx + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +mu-stmt-matches-function?: # stmt : (address statement), function : (address opcode-info) => result/eax : boolean # . prologue 55/push-ebp 89/<- %ebp 4/r32/esp @@ -1062,6 +1256,24 @@ $mu-stmt-matches-function?:end: 5d/pop-to-ebp c3/return +mu-stmt-matches-primitive?: # stmt : (address statement), primitive : (address primitive) => result/eax : boolean + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # . save registers + 51/push-ecx + # return primitive->name == stmt->operation + 8b/-> *(ebp+8) 1/r32/ecx + 8b/-> *(ebp+0xc) 0/r32/eax + (string-equal? *ecx *eax) # => eax +$mu-stmt-matches-primitive?:end: + # . restore registers + 59/pop-to-ecx + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + test-emit-subx-statement-primitive: # Primitive operation on a variable on the stack. # increment foo @@ -1075,7 +1287,7 @@ test-emit-subx-statement-primitive: # # There's a primitive with this info: # name: 'increment' - # inout: int/mem + # inouts: int/mem # value: 'ff 0/subop/increment' # # There's nothing in functions. @@ -1110,10 +1322,12 @@ test-emit-subx-statement-primitive: 89/<- %esi 4/r32/esp # primitives/ebx : primitive 68/push 0/imm32/next - 68/push 0/imm32/body + 68/push 0/imm32/no-imm32 + 68/push 0/imm32/no-r32 + 68/push 1/imm32/rm32-is-first-inout + 68/push "ff 0/subop/increment"/imm32/subx-name 68/push 0/imm32/outputs 51/push-ecx/inouts # hack; in practice we won't have the same var in function definition and call - 68/push "ff 0/subop/increment"/imm32/subx-name 68/push "increment"/imm32/name 89/<- %ebx 4/r32/esp # convert @@ -1134,6 +1348,91 @@ test-emit-subx-statement-primitive: 5d/pop-to-ebp c3/return +test-emit-subx-statement-primitive-register: + # Primitive operation on a variable in a register. + # foo <- increment + # => + # ff 0/subop/increment %eax # sub-optimal, but should suffice + # + # There's a variable on the var stack as follows: + # name: 'foo' + # type: int + # register: 'eax' + # + # There's a primitive with this info: + # name: 'increment' + # inout: int/reg + # value: 'ff 0/subop/increment' + # + # There's nothing in functions. + # + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # setup + (clear-stream _test-output-stream) + (clear-stream _test-output-buffered-file->buffer) + # var-foo/ecx : var in eax + 68/push "eax"/imm32/register + 68/push 0/imm32/no-stack-offset + 68/push 1/imm32/block-depth + 68/push 1/imm32/type-int + 68/push "foo"/imm32 + 89/<- %ecx 4/r32/esp + # vars/edx : (stack 1) + 51/push-ecx/var-foo + 68/push 1/imm32/data-length + 68/push 1/imm32/top + 89/<- %edx 4/r32/esp + # operand/esi : (list var) + 68/push 0/imm32/next + 51/push-ecx/var-foo + 89/<- %esi 4/r32/esp + # stmt/esi : statement + 68/push 0/imm32/next + 56/push-esi/outputs + 68/push 0/imm32/inouts + 68/push "increment"/imm32/operation + 89/<- %esi 4/r32/esp + # formal-var/ebx : var in any register + 68/push Any-register/imm32 + 68/push 0/imm32/no-stack-offset + 68/push 1/imm32/block-depth + 68/push 1/imm32/type-int + 68/push "dummy"/imm32 + 89/<- %ebx 4/r32/esp + # operand/ebx : (list var) + 68/push 0/imm32/next + 53/push-ebx/formal-var + 89/<- %ebx 4/r32/esp + # primitives/ebx : primitive + 68/push 0/imm32/next + 68/push 0/imm32/no-imm32 + 68/push 0/imm32/no-r32 + 68/push 3/imm32/rm32-in-first-output + 68/push "ff 0/subop/increment"/imm32/subx-name + 53/push-ebx/outputs + 68/push 0/imm32/inouts + 68/push "increment"/imm32/name + 89/<- %ebx 4/r32/esp + # convert + (emit-subx-statement _test-output-buffered-file %esi %edx %ebx 0) + (flush _test-output-buffered-file) +#? # dump _test-output-stream {{{ +#? (write 2 "^") +#? (write-stream 2 _test-output-stream) +#? (write 2 "$\n") +#? (rewind-stream _test-output-stream) +#? # }}} + # check output + (check-next-stream-line-equal _test-output-stream "ff 0/subop/increment %eax" "F - test-emit-subx-statement-primitive-register/0") + # . reclaim locals + 81 0/subop/add %esp 0x48/imm32 + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + test-emit-subx-statement-function-call: # Call a function on a variable on the stack. # f foo -- cgit 1.4.1-2-gfad0