6682 - experimental support for streams and slices

Slices contain `addr`s so the same rules apply to them. They can't be stored
in structs and so on. But they may be an efficient temporary while parsing.

Streams are currently a second generic type after arrays, and gradually
strengthening the case to just bite the bullet and support first-class
generics in Mu.
This commit is contained in:
Kartik Agaram 2020-07-28 21:17:32 -07:00
parent e2c22ad4d9
commit b8df5340fa
4 changed files with 213 additions and 12 deletions

View File

@ -1,6 +1,6 @@
# 2-arg version of allocate-array.
allocate-array2: # ad: (addr allocation-descriptor), elem-size: int, array-len: int, out: (addr handle array _)
allocate-array2: # ad: (addr allocation-descriptor), array-len: int, elem-size: int, out: (addr handle array _)
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp

4
400.mu
View File

@ -78,7 +78,7 @@ sig copy-handle src: (handle _T), dest: (addr handle _T)
#sig allocate-array ad: (addr allocation-descriptor), n: int, out: (addr handle _)
sig copy-array ad: (addr allocation-descriptor), src: (addr array _T), out: (addr handle array _T)
#sig zero-out start: (addr byte), size: int
sig new-stream ad: (addr allocation-descriptor), length: int, elemsize: int, out: (addr handle stream _)
#sig new-stream ad: (addr allocation-descriptor), length: int, elemsize: int, out: (addr handle stream _)
sig read-line-buffered f: (addr buffered-file), s: (addr stream byte)
sig read-line f: (addr stream byte), s: (addr stream byte)
sig slice-empty? s: (addr slice) -> result/eax: boolean
@ -156,4 +156,4 @@ sig enable-keyboard-immediate-mode
sig enable-keyboard-type-mode
sig read-key -> result/eax: byte
sig open filename: (addr array byte), write?: boolean, out: (addr handle buffered-file)
sig size in: (addr array _) -> result/eax: int
#sig size in: (addr array _) -> result/eax: int

BIN
apps/mu

Binary file not shown.

View File

@ -407,8 +407,11 @@ Type-id: # (stream (addr array byte))
# Not to be used directly, so we don't include a name here.
0/imm32 # 10 reserved for type parameters; value is (address array byte) in Type-tree-value2.
# Not to be used directly, so we don't include a name here.
# some SubX types deliberately left undefined in Mu; they can only be operated on using SubX primitives
"stream"/imm32 # 11
"slice"/imm32 # 12
# Keep Primitive-type-ids in sync if you add types here.
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
@ -417,7 +420,7 @@ Type-id: # (stream (addr array byte))
0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32 0/imm32
Primitive-type-ids: # (addr int)
0x2c
0x34
# == Type definitions
# Program->types contains some typeinfo for each type definition.
@ -10789,6 +10792,18 @@ $populate-mu-type:parse-element:
(is-mu-array-type? %eax) # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-type:error3/disp32
# if v is a slice, abort
(lookup *esi *(esi+4)) # => eax
(lookup *(eax+8) *(eax+0xc)) # Var-type Var-type => eax
(is-simple-mu-type? %eax 0xc) # slice => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-type:error4/disp32
# if v is a stream, abort (we could support it, but initialization gets even more complex)
(lookup *esi *(esi+4)) # => eax
(lookup *(eax+8) *(eax+0xc)) # Var-type Var-type => eax
(is-mu-stream-type? %eax) # => eax
3d/compare-eax-and 0/imm32/false
0f 85/jump-if-!= $populate-mu-type:error5/disp32
# var tmp/ecx
51/push-ecx
$populate-mu-type:create-typeinfo-fields:
@ -10876,6 +10891,26 @@ $populate-mu-type:error3:
(stop *(ebp+0x14) 1)
# never gets here
$populate-mu-type:error4:
# error("type " t->name ": invalid type 'array'\n")
(write-buffered *(ebp+0x10) "type ")
(type-name *edi) # Typeinfo-id => eax
(write-buffered *(ebp+0x10) %eax)
(write-buffered *(ebp+0x10) ": invalid type 'slice'\n")
(flush *(ebp+0x10))
(stop *(ebp+0x14) 1)
# never gets here
$populate-mu-type:error5:
# error("type " t->name ": invalid type 'array'\n")
(write-buffered *(ebp+0x10) "type ")
(type-name *edi) # Typeinfo-id => eax
(write-buffered *(ebp+0x10) %eax)
(write-buffered *(ebp+0x10) ": invalid type 'stream'\n")
(flush *(ebp+0x10))
(stop *(ebp+0x14) 1)
# never gets here
type-name: # index: int -> result/eax: (addr array byte)
# . prologue
55/push-ebp
@ -11085,6 +11120,13 @@ compute-size-of-type-id: # t: type-id -> result/eax: int
b8/copy-to-eax 8/imm32
eb/jump $compute-size-of-type-id:end/disp8 # eax changes type from type-id to int
}
# if t is a slice, return 8
3d/compare-eax-and 0xc/imm32/slice
{
75/jump-if-!= break/disp8
b8/copy-to-eax 8/imm32
eb/jump $compute-size-of-type-id:end/disp8 # eax changes type from type-id to int
}
# if t is a user-defined type, compute its size
# TODO: support non-atom type
(find-typeinfo %eax %ecx)
@ -12773,6 +12815,14 @@ size-of: # v: (addr var) -> result/eax: int
(size-of-array %ecx) # => eax
eb/jump $size-of:end/disp8
}
# if is-mu-stream?(t) return size-of-stream(t)
{
(is-mu-stream? %ecx) # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
(size-of-stream %ecx) # => eax
eb/jump $size-of:end/disp8
}
# if (!t->is-atom?) t = lookup(t->left)
{
81 7/subop/compare *ecx 0/imm32/false # Type-tree-is-atom
@ -12812,6 +12862,14 @@ size-of-deref: # v: (addr var) -> result/eax: int
(size-of-array %ecx) # => eax
eb/jump $size-of-deref:end/disp8
}
# if is-mu-stream?(t) return size-of-stream(t)
{
(is-mu-stream? %ecx) # => eax
3d/compare-eax-and 0/imm32/false
74/jump-if-= break/disp8
(size-of-stream %ecx) # => eax
eb/jump $size-of-deref:end/disp8
}
# if (!t->is-atom?) t = lookup(t->left)
{
81 7/subop/compare *ecx 0/imm32/false # Type-tree-is-atom
@ -12859,6 +12917,7 @@ $is-mu-array?:end:
5d/pop-to-ebp
c3/return
# size of a statically allocated array where the size is part of the type expression
size-of-array: # a: (addr type-tree) -> result/eax: int
# . prologue
55/push-ebp
@ -12892,6 +12951,50 @@ $size-of-array:end:
5d/pop-to-ebp
c3/return
is-mu-stream?: # t: (addr type-tree) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# . save registers
51/push-ecx
# ecx = t
8b/-> *(ebp+8) 1/r32/ecx
# if t->is-atom?, return false
81 7/subop/compare *ecx 0/imm32/false # Type-tree-is-atom
75/jump-if-!= $is-mu-stream?:return-false/disp8
# if !t->left->is-atom?, return false
(lookup *(ecx+4) *(ecx+8)) # Type-tree-left Type-tree-left => eax
81 7/subop/compare *eax 0/imm32/false # Type-tree-is-atom
74/jump-if-= $is-mu-stream?:return-false/disp8
# return t->left->value == stream
81 7/subop/compare *(eax+4) 0xb/imm32/stream-type-id # Type-tree-value
0f 94/set-if-= %al
81 4/subop/and %eax 0xff/imm32
eb/jump $is-mu-stream?:end/disp8
$is-mu-stream?:return-false:
b8/copy-to-eax 0/imm32/false
$is-mu-stream?:end:
# . restore registers
59/pop-to-ecx
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
# size of a statically allocated stream where the size is part of the type expression
size-of-stream: # a: (addr type-tree) -> result/eax: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
(size-of-array *(ebp+8)) # assumes we ignore the actual type name 'array' in the type
05/add-to-eax 8/imm32 # for read/write pointers
$size-of-stream:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
size-of-type-id: # t: type-id -> result/eax: int
# . prologue
55/push-ebp
@ -14268,7 +14371,7 @@ emit-subx-var-def: # out: (addr buffered-file), stmt: (addr stmt)
# v->offset = *Curr-local-stack-offset
8b/-> *Curr-local-stack-offset 0/r32/eax
89/<- *(ecx+0x14) 0/r32/eax # Var-offset
# if v is an array, do something special
# if v is an array, do something special to initialize it
{
(lookup *(ecx+8) *(ecx+0xc)) # Var-type Var-type => eax
(is-mu-array? %eax) # => eax
@ -14276,14 +14379,26 @@ emit-subx-var-def: # out: (addr buffered-file), stmt: (addr stmt)
0f 84/jump-if-= break/disp32
# var array-size-without-size/edx: int = n-4
81 5/subop/subtract %edx 4/imm32
#
(emit-array-data-initialization *(ebp+8) %edx)
e9/jump $emit-subx-var-def:end/disp32
}
# another special-case for initializing streams
# a stream is an array with 2 extra pointers
{
(lookup *(ecx+8) *(ecx+0xc)) # Var-type Var-type => eax
(is-mu-stream? %eax) # => eax
3d/compare-eax-and 0/imm32/false
0f 84/jump-if-= break/disp32
# var array-size-without-size/edx: int = n-12
81 5/subop/subtract %edx 0xc/imm32
(emit-array-data-initialization *(ebp+8) %edx)
# emit read and write pointers
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "(push-n-zero-bytes ")
(write-int32-hex-buffered *(ebp+8) %edx)
(write-buffered *(ebp+8) ")\n")
(write-buffered *(ebp+8) "68/push 0/imm32\n")
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "68/push ")
(write-int32-hex-buffered *(ebp+8) %edx)
(write-buffered *(ebp+8) "/imm32\n")
(write-buffered *(ebp+8) "68/push 0/imm32\n")
#
eb/jump $emit-subx-var-def:end/disp8
}
# while n > 0
@ -14307,6 +14422,25 @@ $emit-subx-var-def:end:
5d/pop-to-ebp
c3/return
emit-array-data-initialization: # out: (addr buffered-file), n: int
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
#
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "(push-n-zero-bytes ")
(write-int32-hex-buffered *(ebp+8) *(ebp+0xc))
(write-buffered *(ebp+8) ")\n")
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "68/push ")
(write-int32-hex-buffered *(ebp+8) *(ebp+0xc))
(write-buffered *(ebp+8) "/imm32\n")
$emit-array-data-initialization:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
emit-subx-stmt: # out: (addr buffered-file), stmt: (addr stmt), primitives: (addr primitive), err: (addr buffered-file), ed: (addr exit-descriptor)
# . prologue
55/push-ebp
@ -14373,6 +14507,15 @@ emit-subx-stmt: # out: (addr buffered-file), stmt: (addr stmt), primitives: (ad
(translate-mu-populate-stmt *(ebp+8) *(ebp+0xc) *(ebp+0x14) *(ebp+0x18))
e9/jump $emit-subx-stmt:end/disp32
}
# allocate stream
{
# if (!string-equal?(stmt->operation, "populate-stream")) break
(string-equal? %ecx "populate-stream") # => eax
3d/compare-eax-and 0/imm32
0f 84/jump-if-= break/disp32
(translate-mu-populate-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:
@ -15210,6 +15353,42 @@ $translate-mu-populate-stmt:end:
5d/pop-to-ebp
c3/return
translate-mu-populate-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 target/edi: (addr stmt-var) = stmt->inouts[0]
(lookup *(esi+0xc) *(esi+0x10)) # Stmt1-inouts Stmt1-inouts => eax
89/<- %edi 0/r32/eax
# var len/ecx: (addr stmt-var) = stmt->inouts[1]
(lookup *(edi+8) *(edi+0xc)) # Stmt-var-next Stmt-var-next => eax
89/<- %ecx 0/r32/eax
#
(emit-indent *(ebp+8) *Curr-block-depth)
(write-buffered *(ebp+8) "(new-stream Heap ")
(addr-handle-array-payload-size %edi *(ebp+0x10) *(ebp+0x14)) # => eax
(write-int32-hex-buffered *(ebp+8) %eax)
(emit-subx-call-operand *(ebp+8) %ecx)
(emit-subx-call-operand *(ebp+8) %edi)
(write-buffered *(ebp+8) ")\n")
$translate-mu-populate-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
@ -19568,6 +19747,28 @@ $is-mu-array-type?:end:
5d/pop-to-ebp
c3/return
is-mu-stream-type?: # a: (addr type-tree) -> result/eax: boolean
# . prologue
55/push-ebp
89/<- %ebp 4/r32/esp
# eax = a
8b/-> *(ebp+8) 0/r32/eax
# if (!a->is-atom?) a = a->left
81 7/subop/compare *eax 0/imm32/false # Type-tree-is-atom
{
75/jump-if-!= break/disp8
(lookup *(eax+4) *(eax+8)) # Type-tree-left Type-tree-left => eax
}
# return (a->value == stream)
81 7/subop/compare *(eax+4) 0xb/imm32/stream # Type-tree-value
0f 94/set-byte-if-= %al
81 4/subop/and %eax 0xff/imm32
$is-mu-stream-type?:end:
# . epilogue
89/<- %esp 5/r32/ebp
5d/pop-to-ebp
c3/return
test-emit-subx-stmt-primitive:
# Primitive operation on a variable on the stack.
# increment foo