about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik Agaram <vc@akkartik.com>2020-07-29 21:41:29 -0700
committerKartik Agaram <vc@akkartik.com>2020-07-29 21:41:29 -0700
commitbae22d720f6a6f3922c29cd7dc4218c7564bbed9 (patch)
tree955d9b13dbeec75a6b0fef6dddb9638c0c9c4d80
parent129bb8117b02956714c1be124c7ce0f27393726c (diff)
downloadmu-bae22d720f6a6f3922c29cd7dc4218c7564bbed9.tar.gz
6684 - experimental primitives for streams
This is a hacky special case. The alternative would be more general support
for generics.

One observation: we might be able to type-check some primitives using `sig`s.
Only if they don't return anything, since primitives usually need to support
arbitrary registers. I'm not doing that yet, though. It eliminates the
possibility of writing tests for them in mu.subx, which can't see 400.mu.
But it's an alternative:

  sig allocate out: (addr handle _)
  sig populate out: (addr handle array _), n: int
  sig populate-stream out: (addr handle stream _), n: int
  sig read-from-stream s: (addr stream _T), out: (addr _T)
  sig write-to-stream s: (addr stream _T), in: (addr _T)

We could write the tests in Mu. But then we're testing behavior rather
than the code generated. There are trade-offs. By performing type-checking
in mu.subx I retain the option to write both kinds of tests.
-rw-r--r--309stream.subx108
-rw-r--r--402stream.mu15
-rwxr-xr-xapps/mubin363666 -> 365432 bytes
-rw-r--r--apps/mu.subx197
4 files changed, 320 insertions, 0 deletions
diff --git a/309stream.subx b/309stream.subx
new file mode 100644
index 00000000..3d38eda9
--- /dev/null
+++ b/309stream.subx
@@ -0,0 +1,108 @@
+# Some unsafe methods not intended to be used directly in SubX, only through
+# Mu after proper type-checking.
+
+write-to-stream:  # s: (addr stream), in: (addr byte), n: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    57/push-edi
+    # edi = s
+    8b/-> *(ebp+8) 7/r32/edi
+    # var swrite/edx: int = s->write
+    8b/-> *edi 2/r32/edx
+    # if (swrite + n > s->size) abort
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    01/add-to %ecx 2/r32/edx
+    3b/compare 1/r32/ecx *(edi+8)
+    0f 8f/jump-if-> $write-to-stream:abort/disp32
+    # var out/edx: (addr byte) = s->data + s->write
+    8d/copy-address *(edi+edx+0xc) 2/r32/edx
+    # var outend/ebx: (addr byte) = out + n
+    8b/-> *(ebp+0x10) 3/r32/ebx
+    8d/copy-address *(edx+ebx) 3/r32/ebx
+    # eax = in
+    8b/-> *(ebp+0xc) 0/r32/eax
+    # var inend/ecx: (addr byte) = in + n
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    8d/copy-address *(eax+ecx) 1/r32/ecx
+    #
+    (_append-4  %edx %ebx  %eax %ecx)  # => eax
+    # s->write += n
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    01/add-to *edi 1/r32/ecx
+$write-to-stream:end:
+    # . restore registers
+    5f/pop-to-edi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$write-to-stream:abort:
+    (write-buffered Stderr "write-to-stream: stream full\n")
+    (flush Stderr)
+    bb/copy-to-ebx 1/imm32
+    (syscall_exit)
+    # never gets here
+
+read-from-stream:  # s: (addr stream), out: (addr byte), n: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    52/push-edx
+    53/push-ebx
+    56/push-esi
+    # esi = s
+    8b/-> *(ebp+8) 6/r32/esi
+    # var sread/edx: int = s->read
+    8b/-> *(esi+4) 2/r32/edx
+    # if (sread + n > s->write) abort
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    01/add-to %ecx 2/r32/edx
+    3b/compare 1/r32/ecx *esi
+    0f 8f/jump-if-> $read-from-stream:abort/disp32
+    # var in/edx: (addr byte) = s->data + s->read
+    8d/copy-address *(esi+edx+0xc) 2/r32/edx
+    # var inend/ebx: (addr byte) = in + n
+    8b/-> *(ebp+0x10) 3/r32/ebx
+    8d/copy-address *(edx+ebx) 3/r32/ebx
+    # eax = out
+    8b/-> *(ebp+0xc) 0/r32/eax
+    # var outend/ecx: (addr byte) = out + n
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    8d/copy-address *(eax+ecx) 1/r32/ecx
+    #
+    (_append-4  %eax %ecx  %edx %ebx)  # => eax
+    # s->read += n
+    8b/-> *(ebp+0x10) 1/r32/ecx
+    01/add-to *(esi+4) 1/r32/ecx
+$read-from-stream:end:
+    # . restore registers
+    5e/pop-to-esi
+    5b/pop-to-ebx
+    5a/pop-to-edx
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+$read-from-stream:abort:
+    (write-buffered Stderr "read-from-stream: stream empty\n")
+    (flush Stderr)
+    bb/copy-to-ebx 1/imm32
+    (syscall_exit)
+    # never gets here
diff --git a/402stream.mu b/402stream.mu
new file mode 100644
index 00000000..e7661658
--- /dev/null
+++ b/402stream.mu
@@ -0,0 +1,15 @@
+# Tests for Mu's stream primitives.
+
+fn test-stream {
+  # write an int to a stream, then read it back
+  var s: (stream int 4)
+  var s2/ecx: (addr stream int 4) <- address s
+  var x: int
+  copy-to x, 0x34
+  var x2/edx: (addr int) <- address x
+  write-to-stream s2, x2
+  var y: int
+  var y2/ebx: (addr int) <- address y
+  read-from-stream s2, y2
+  check-ints-equal y, 0x34, "F - test-stream"
+}
diff --git a/apps/mu b/apps/mu
index 90d70f35..a86811c9 100755
--- a/apps/mu
+++ b/apps/mu
Binary files differdiff --git a/apps/mu.subx b/apps/mu.subx
index 4a0f6c54..3c64c467 100644
--- a/apps/mu.subx
+++ b/apps/mu.subx
@@ -11699,6 +11699,18 @@ has-primitive-name?:  # stmt: (addr stmt) -> result/eax: boolean
     (string-equal? %esi "populate")  # => eax
     3d/compare-eax-and 0/imm32/false
     0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "populate-stream") return true
+    (string-equal? %esi "populate-stream")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "read-from-stream") return true
+    (string-equal? %esi "read-from-stream")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
+    # if (name == "write-to-stream") return true
+    (string-equal? %esi "write-to-stream")  # => eax
+    3d/compare-eax-and 0/imm32/false
+    0f 85/jump-if-!= $has-primitive-name?:end/disp32
     # var curr/ecx: (addr primitive) = Primitives
     b9/copy-to-ecx Primitives/imm32
     {
@@ -11820,6 +11832,30 @@ check-mu-primitive:  # stmt: (addr stmt), fn: (addr function), err: (addr buffer
       (check-mu-populate-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
       e9/jump $check-mu-primitive:end/disp32
     }
+    # if (op == "populate-stream") check-mu-populate-stream-stmt
+    {
+      (string-equal? %ecx "populate-stream")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-populate-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "read-from-stream") check-mu-read-from-stream-stmt
+    {
+      (string-equal? %ecx "read-from-stream")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-read-from-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
+    # if (op == "write-to-stream") check-mu-write-to-stream-stmt
+    {
+      (string-equal? %ecx "write-to-stream")  # => eax
+      3d/compare-eax-and 0/imm32/false
+      74/jump-if-= break/disp8
+      (check-mu-write-to-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
+      e9/jump $check-mu-primitive:end/disp32
+    }
     # otherwise check-numberlike-stmt
     (check-mu-numberlike-primitive *(ebp+8) *(ebp+0xc) *(ebp+0x10) *(ebp+0x14))
 $check-mu-primitive:end:
@@ -12399,6 +12435,42 @@ $check-mu-populate-stmt:end:
     5d/pop-to-ebp
     c3/return
 
+check-mu-populate-stream-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+$check-mu-populate-stream-stmt:end:
+    # . restore registers
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-read-from-stream-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+$check-mu-read-from-stream-stmt:end:
+    # . restore registers
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+check-mu-write-to-stream-stmt:  # stmt: (addr stmt), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+$check-mu-write-to-stream-stmt:end:
+    # . restore registers
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
 check-mu-call:  # stmt: (addr stmt), callee: (addr function), fn: (addr function), err: (addr buffered-file), ed: (addr exit-descriptor)
     # . prologue
     55/push-ebp
@@ -14512,6 +14584,24 @@ emit-subx-stmt:  # out: (addr buffered-file), stmt: (addr stmt), primitives: (ad
       (translate-mu-populate-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))
       e9/jump $emit-subx-stmt:end/disp32
     }
+    # read from stream
+    {
+      # if (!string-equal?(stmt->operation, "read-from-stream")) break
+      (string-equal? %ecx "read-from-stream")  # => eax
+      3d/compare-eax-and 0/imm32
+      0f 84/jump-if-= break/disp32
+      (translate-mu-read-from-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
+    # write to stream
+    {
+      # if (!string-equal?(stmt->operation, "write-to-stream")) break
+      (string-equal? %ecx "write-to-stream")  # => eax
+      3d/compare-eax-and 0/imm32
+      0f 84/jump-if-= break/disp32
+      (translate-mu-write-to-stream-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))
+      e9/jump $emit-subx-stmt:end/disp32
+    }
     # - if stmt matches a primitive, emit it
     {
 $emit-subx-stmt:check-for-primitive:
@@ -15313,6 +15403,36 @@ $addr-handle-payload-size:end:
     5d/pop-to-ebp
     c3/return
 
+addr-payload-size:  # s: (addr stmt-var), err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: int
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # var t/eax: (addr type-tree) = s->value->type
+    8b/-> *(ebp+8) 0/r32/eax
+    (lookup *eax *(eax+4))  # Stmt-var-value Stmt-var-value => eax
+    (lookup *(eax+8) *(eax+0xc))  # Var-type Var-type => eax
+    # TODO: check eax != 0
+    # TODO: check !t->is-atom?
+    # TODO: check t->left == addr
+    # t = t->right
+$addr-payload-size:skip-addr:
+    (lookup *(eax+0xc) *(eax+0x10))  # Type-tree-right Type-tree-right => eax
+    # TODO: check eax != 0
+    # if !t->is-atom? t = t->left
+    81 7/subop/compare *eax 0/imm32/false
+    {
+      75/jump-if-!= break/disp8
+      (lookup *(eax+4) *(eax+8))  # Type-tree-left Type-tree-left => eax
+    }
+    # TODO: check t->is-atom?
+    # return size(t->value)
+    (size-of-type-id *(eax+4))  # Type-tree-value => eax
+$addr-payload-size:end:
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
 translate-mu-populate-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
     # . prologue
     55/push-ebp
@@ -15385,6 +15505,83 @@ $translate-mu-populate-stream-stmt:end:
     5d/pop-to-ebp
     c3/return
 
+translate-mu-read-from-stream-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var stream/ecx: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %ecx 0/r32/eax
+    # var target/edi: (addr stmt-var) = stmt->inouts[1]
+    (lookup *(ecx+8) *(ecx+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %edi 0/r32/eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(read-from-stream")
+    (emit-subx-call-operand *(ebp+8) %ecx)
+    (emit-subx-call-operand *(ebp+8) %edi)
+    (write-buffered *(ebp+8) Space)
+    (addr-payload-size %edi *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) ")\n")
+$translate-mu-read-from-stream-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
+translate-mu-write-to-stream-stmt:  # out: (addr buffered-file), stmt: (addr stmt), err: (addr buffered-file), ed: (addr exit-descriptor)
+    # . prologue
+    55/push-ebp
+    89/<- %ebp 4/r32/esp
+    # . save registers
+    50/push-eax
+    51/push-ecx
+    56/push-esi
+    57/push-edi
+    # esi = stmt
+    8b/-> *(ebp+0xc) 6/r32/esi
+    # var stream/ecx: (addr stmt-var) = stmt->inouts[0]
+    (lookup *(esi+0xc) *(esi+0x10))  # Stmt1-inouts Stmt1-inouts => eax
+    89/<- %ecx 0/r32/eax
+    # var target/edi: (addr stmt-var) = stmt->inouts[1]
+    (lookup *(ecx+8) *(ecx+0xc))  # Stmt-var-next Stmt-var-next => eax
+    89/<- %edi 0/r32/eax
+    #
+    (emit-indent *(ebp+8) *Curr-block-depth)
+    (write-buffered *(ebp+8) "(write-to-stream")
+    (emit-subx-call-operand *(ebp+8) %ecx)
+    (flush *(ebp+8))
+    (emit-subx-call-operand *(ebp+8) %edi)
+    (flush *(ebp+8))
+    (write-buffered *(ebp+8) Space)
+    (flush *(ebp+8))
+    (addr-payload-size %edi *(ebp+0x10) *(ebp+0x14))  # => eax
+    (write-int32-hex-buffered *(ebp+8) %eax)
+    (write-buffered *(ebp+8) ")\n")
+$translate-mu-write-to-stream-stmt:end:
+    # . restore registers
+    5f/pop-to-edi
+    5e/pop-to-esi
+    59/pop-to-ecx
+    58/pop-to-eax
+    # . epilogue
+    89/<- %esp 5/r32/ebp
+    5d/pop-to-ebp
+    c3/return
+
 addr-handle-array-payload-size:  # s: (addr stmt-var), err: (addr buffered-file), ed: (addr exit-descriptor) -> result/eax: int
     # . prologue
     55/push-ebp