diff --git a/apps/mu b/apps/mu index 611e1302..466ec552 100755 Binary files a/apps/mu and b/apps/mu differ diff --git a/apps/mu.subx b/apps/mu.subx index 3d6c9dc2..1d1d60ab 100644 --- a/apps/mu.subx +++ b/apps/mu.subx @@ -2436,14 +2436,15 @@ test-convert-length-of-array: (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:loop:" "F - test-convert-length-of-array/5") (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-length-of-array/6") (check-next-stream-line-equal _test-output-stream " 8b/-> *(ebp+0x00000008) 0x00000000/r32" "F - test-convert-length-of-array/7") - (check-next-stream-line-equal _test-output-stream " 8b/-> *eax 0x00000000/r32" "F - test-convert-length-of-array/9") - (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array/11") - (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-length-of-array/12") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-length-of-array/13") - (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-length-of-array/14") - (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-length-of-array/15") - (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-length-of-array/16") - (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-length-of-array/17") + (check-next-stream-line-equal _test-output-stream " 8b/-> *eax 0x00000000/r32" "F - test-convert-length-of-array/8") + (check-next-stream-line-equal _test-output-stream " c1/shift 5/subop/>> %eax 0x00000002/imm32" "F - test-convert-length-of-array/9") + (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array/10") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-length-of-array/11") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-length-of-array/12") + (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-length-of-array/13") + (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-length-of-array/14") + (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-length-of-array/15") + (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-length-of-array/16") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp @@ -2484,14 +2485,15 @@ test-convert-length-of-array-on-stack: (check-next-stream-line-equal _test-output-stream " 68/push 0x0000000c/imm32" "F - test-convert-length-of-array-on-stack/7") (check-next-stream-line-equal _test-output-stream " ff 6/subop/push %eax" "F - test-convert-length-of-array-on-stack/8") (check-next-stream-line-equal _test-output-stream " 8b/-> *(ebp+0xfffffff0) 0x00000000/r32" "F - test-convert-length-of-array-on-stack/9") - (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array-on-stack/10") - (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000010/imm32" "F - test-convert-length-of-array-on-stack/11") - (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-length-of-array-on-stack/12") - (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-length-of-array-on-stack/13") - (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-length-of-array-on-stack/14") - (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-length-of-array-on-stack/15") - (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-length-of-array-on-stack/16") - (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-length-of-array-on-stack/17") + (check-next-stream-line-equal _test-output-stream " c1/shift 5/subop/>> %eax 0x00000002/imm32" "F - test-convert-length-of-array-on-stack/10") + (check-next-stream-line-equal _test-output-stream " 8f 0/subop/pop %eax" "F - test-convert-length-of-array-on-stack/11") + (check-next-stream-line-equal _test-output-stream " 81 0/subop/add %esp 0x00000010/imm32" "F - test-convert-length-of-array-on-stack/12") + (check-next-stream-line-equal _test-output-stream " }" "F - test-convert-length-of-array-on-stack/13") + (check-next-stream-line-equal _test-output-stream "$foo:0x00000001:break:" "F - test-convert-length-of-array-on-stack/14") + (check-next-stream-line-equal _test-output-stream " # . epilogue" "F - test-convert-length-of-array-on-stack/15") + (check-next-stream-line-equal _test-output-stream " 89/<- %esp 5/r32/ebp" "F - test-convert-length-of-array-on-stack/16") + (check-next-stream-line-equal _test-output-stream " 5d/pop-to-ebp" "F - test-convert-length-of-array-on-stack/17") + (check-next-stream-line-equal _test-output-stream " c3/return" "F - test-convert-length-of-array-on-stack/18") # . epilogue 89/<- %esp 5/r32/ebp 5d/pop-to-ebp @@ -8842,28 +8844,31 @@ translate-mu-length-stmt: # out: (addr buffered-file), stmt: (addr stmt) # . save registers 50/push-eax 51/push-ecx + 52/push-edx + 53/push-ebx # ecx = stmt 8b/-> *(ebp+0xc) 1/r32/ecx # (emit-indent *(ebp+8) *Curr-block-depth) (write-buffered *(ebp+8) "8b/-> *") - # var base/eax: (handle var) = inouts[0] + # var base/ebx: (addr var) = inouts[0] (lookup *(ecx+0xc) *(ecx+0x10)) # Stmt1-inouts Stmt1-inouts => eax (lookup *eax *(eax+4)) # Stmt-var-value Stmt-var-value => eax + 89/<- %ebx 0/r32/eax # if base is an (addr array ...) in a register { - 81 7/subop/compare *(eax+0x18)) 0/imm32 # Var-register + 81 7/subop/compare *(ebx+0x18)) 0/imm32 # Var-register 74/jump-if-= break/disp8 - (lookup *(eax+0x18) *(eax+0x1c)) # Var-register Var-register => eax + (lookup *(ebx+0x18) *(ebx+0x1c)) # Var-register Var-register => eax (write-buffered *(ebp+8) %eax) eb/jump $translate-mu-length-stmt:emit-output/disp8 } # otherwise if base is an (array ...) on the stack { - 81 7/subop/compare *(eax+0x14)) 0/imm32 # Var-offset + 81 7/subop/compare *(ebx+0x14)) 0/imm32 # Var-offset 74/jump-if-= break/disp8 (write-buffered *(ebp+8) "(ebp+") - (print-int32-buffered *(ebp+8) *(eax+0x14)) # Var-offset + (print-int32-buffered *(ebp+8) *(ebx+0x14)) # Var-offset (write-buffered *(ebp+8) ")") } $translate-mu-length-stmt:emit-output: @@ -8871,12 +8876,40 @@ $translate-mu-length-stmt:emit-output: # outputs[0] "/r32" (lookup *(ecx+0x14) *(ecx+0x18)) # Stmt1-outputs Stmt1-outputs => eax (lookup *eax *(eax+4)) # Stmt-var-value Stmt-var-value => eax + # edx = outputs[0]->register (lookup *(eax+0x18) *(eax+0x1c)) # Var-register Var-register => eax - (get Registers %eax 0xc "Registers") # => eax + 89/<- %edx 0/r32/eax + # + (get Registers %edx 0xc "Registers") # => eax (print-int32-buffered *(ebp+8) *eax) (write-buffered *(ebp+8) "/r32\n") +$translate-mu-length-stmt:check-power-of-2: + # ecx = size-of(element-type(base)) + (array-element-type-id %ebx) # => eax + (size-of-type-id %eax) # => eax + 89/<- %ecx 0/r32/eax + # + (power-of-2? %ecx) # => eax + 3d/compare-eax-and 0/imm32/false + { + 0f 84/jump-if-= break/disp32 +$translate-mu-length-stmt:is-power-of-2: + (emit-indent *(ebp+8) *Curr-block-depth) + (write-buffered *(ebp+8) "c1/shift 5/subop/>> %") + (write-buffered *(ebp+8) %edx) + (write-buffered *(ebp+8) Space) + (num-shift-rights %ecx) # => eax + (print-int32-buffered *(ebp+8) %eax) + (write-buffered *(ebp+8) "/imm32\n") + eb/jump $translate-mu-length-stmt:end/disp8 + } + { + 75/jump-if-!= break/disp8 +$translate-mu-length-stmt:not-power-of-2: + } $translate-mu-length-stmt:end: # . restore registers + 5b/pop-to-ebx 59/pop-to-ecx 58/pop-to-eax # . epilogue @@ -9310,6 +9343,39 @@ $array-element-type-id:end: 5d/pop-to-ebp c3/return +power-of-2?: # n: int -> result/eax: boolean + # precondition: n is positive + # . prologue + 55/push-ebp + 89/<- %ebp 4/r32/esp + # eax = n + 8b/-> *(ebp+8) 0/r32/eax + # if (n < 0) abort + 3d/compare-eax-with 0/imm32 + 0f 8c/jump-if-< $power-of-2?:abort/disp32 + # var tmp/eax: int = n-1 + 48/decrement-eax + # var tmp2/eax: int = n & tmp + 23/and-> *(ebp+8) 0/r32/eax + # return (tmp2 == 0) + 3d/compare-eax-and 0/imm32 + 0f 94/set-byte-if-= %al + 81 4/subop/and %eax 0xff/imm32 +$power-of-2?:end: + # . epilogue + 89/<- %esp 5/r32/ebp + 5d/pop-to-ebp + c3/return + +$power-of-2?:abort: + (write-buffered Stderr "power-of-2?: negative number\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 + num-shift-rights: # n: int -> result/eax: int # precondition: n is a positive power of 2 # . prologue diff --git a/mu_instructions b/mu_instructions index 1037e8e3..754f51bb 100644 --- a/mu_instructions +++ b/mu_instructions @@ -183,11 +183,35 @@ var/reg: (offset T) <- compute-offset arr: (addr array T), idx: int # arr var/reg <- index arr/rega: (addr array T), o/rego: offset => "8d/copy-address *(" rega "+" rego "+4) " reg "/r32" -Computing the length of an array can get complex. +Computing the length of an array is complex. var/reg <- length arr/reg2: (addr array T) | if T is byte (TODO) => "8b/-> *" reg2 " " reg "/r32" + | if size-of(T) is 4 or 8 or 16 or 32 or 64 or 128 + => "8b/-> *" reg2 " " reg "/r32" + "c1/shift 5/subop/logic-right %" reg " " log2(size-of(T)) "/imm8" + | otherwise (TODO) + x86 has no instruction to divide by a literal, so + we need up to 3 extra registers! eax/edx for division and say ecx + => if reg is not eax + "50/push-eax" + if reg is not ecx + "51/push-ecx" + if reg is not edx + "52/push-edx" + "8b/-> *" reg2 " eax/r32" + "31/xor %edx 2/r32/edx" # sign-extend, but array size can't be negative + "b9/copy-to-ecx " size-of(T) "/imm32" + "f7 7/subop/idiv-eax-edx-by %ecx" + if reg is not eax + "89/<- %" reg " 0/r32/eax" + if reg is not edx + "5a/pop-to-edx" + if reg is not ecx + "59/pop-to-ecx" + if reg is not eax + "58/pop-to-eax" # User-defined types