@effekt-lang/effekt 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/effekt +0 -0
- package/libraries/common/array.effekt +66 -8
- package/libraries/common/buffer.effekt +32 -33
- package/libraries/common/bytearray.effekt +40 -8
- package/libraries/common/queue.effekt +62 -62
- package/libraries/common/ref.effekt +62 -9
- package/libraries/common/stringbuffer.effekt +1 -1
- package/libraries/js/effekt_runtime.js +72 -69
- package/libraries/llvm/main.c +0 -2
- package/libraries/llvm/rts.ll +2 -30
- package/licenses/eclipse public license, version 2.0 - epl-2.0.html +23 -23
- package/licenses/mit license - mit-license.html +51 -31
- package/licenses/the bsd license - bsd-license.html +50 -32
- package/licenses/the mit license - mit.html +51 -31
- package/package.json +1 -1
- package/libraries/llvm/array.c +0 -57
- package/libraries/llvm/ref.c +0 -47
package/bin/effekt
CHANGED
|
Binary file
|
|
@@ -8,14 +8,54 @@ import option
|
|
|
8
8
|
/// A mutable 0-indexed fixed-sized array.
|
|
9
9
|
extern type Array[T]
|
|
10
10
|
|
|
11
|
+
/** We represent arrays like positive types.
|
|
12
|
+
* The tag is 0 and the obj points to memory with the following layout:
|
|
13
|
+
*
|
|
14
|
+
* +--[ Header ]--+------+------------+
|
|
15
|
+
* | Rc | Eraser | Size | Fields ... |
|
|
16
|
+
* +--------------+------+------------+
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
extern llvm """
|
|
20
|
+
declare noalias ptr @calloc(i64, i64)
|
|
21
|
+
|
|
22
|
+
define void @array_erase_fields(ptr %array_pointer) {
|
|
23
|
+
entry:
|
|
24
|
+
%data_pointer = getelementptr inbounds i64, ptr %array_pointer, i64 1
|
|
25
|
+
%size = load i64, ptr %array_pointer, align 8
|
|
26
|
+
%size_eq_0 = icmp eq i64 %size, 0
|
|
27
|
+
br i1 %size_eq_0, label %exit, label %loop
|
|
28
|
+
loop:
|
|
29
|
+
%loop_phi = phi i64 [ %inc, %loop ], [ 0, %entry ]
|
|
30
|
+
|
|
31
|
+
%element_pointer = getelementptr inbounds %Pos, ptr %data_pointer, i64 %loop_phi
|
|
32
|
+
%element = load %Pos, ptr %element_pointer
|
|
33
|
+
|
|
34
|
+
call void @erasePositive(%Pos %element)
|
|
35
|
+
%inc = add nuw i64 %loop_phi, 1
|
|
36
|
+
%cmp = icmp ult i64 %inc, %size
|
|
37
|
+
br i1 %cmp, label %loop, label %exit
|
|
38
|
+
exit:
|
|
39
|
+
ret void
|
|
40
|
+
}
|
|
41
|
+
"""
|
|
42
|
+
|
|
11
43
|
/// Allocates a new array of size `size`, keeping its values _undefined_.
|
|
12
44
|
/// Prefer using `array` constructor instead to ensure that values are defined.
|
|
13
45
|
extern global def allocate[T](size: Int): Array[T] =
|
|
14
46
|
js "(new Array(${size}))"
|
|
15
47
|
chez "(make-vector ${size})" // creates an array filled with 0s on CS
|
|
16
48
|
llvm """
|
|
17
|
-
%
|
|
18
|
-
|
|
49
|
+
%size0 = shl i64 ${size}, 4
|
|
50
|
+
%size1 = add i64 %size0, 24
|
|
51
|
+
%calloc = tail call noalias ptr @calloc(i64 %size1, i64 1)
|
|
52
|
+
%eraser_pointer = getelementptr ptr, ptr %calloc, i64 1
|
|
53
|
+
%size_pointer = getelementptr ptr, ptr %calloc, i64 2
|
|
54
|
+
store i64 0, ptr %calloc
|
|
55
|
+
store ptr @array_erase_fields, ptr %eraser_pointer
|
|
56
|
+
store i64 ${size}, ptr %size_pointer
|
|
57
|
+
%ret_pos = insertvalue %Pos { i64 0, ptr poison }, ptr %calloc, 1
|
|
58
|
+
ret %Pos %ret_pos
|
|
19
59
|
"""
|
|
20
60
|
vm "array::allocate(Int)"
|
|
21
61
|
|
|
@@ -49,8 +89,12 @@ extern pure def size[T](arr: Array[T]): Int =
|
|
|
49
89
|
js "${arr}.length"
|
|
50
90
|
chez "(vector-length ${arr})"
|
|
51
91
|
llvm """
|
|
52
|
-
%
|
|
53
|
-
|
|
92
|
+
%pos_data = extractvalue %Pos ${arr}, 1
|
|
93
|
+
%size_pointer = getelementptr inbounds i64, ptr %pos_data, i64 2
|
|
94
|
+
%size = load i64, ptr %size_pointer
|
|
95
|
+
|
|
96
|
+
tail call void @erasePositive(%Pos ${arr})
|
|
97
|
+
ret i64 %size
|
|
54
98
|
"""
|
|
55
99
|
vm "array::size[T](Array[T])"
|
|
56
100
|
|
|
@@ -62,8 +106,15 @@ extern global def unsafeGet[T](arr: Array[T], index: Int): T =
|
|
|
62
106
|
js "${arr}[${index}]"
|
|
63
107
|
chez "(vector-ref ${arr} ${index})"
|
|
64
108
|
llvm """
|
|
65
|
-
%
|
|
66
|
-
|
|
109
|
+
%pos_data = extractvalue %Pos ${arr}, 1
|
|
110
|
+
|
|
111
|
+
%data_pointer = getelementptr inbounds ptr, ptr %pos_data, i64 3
|
|
112
|
+
%element_pointer = getelementptr inbounds %Pos, ptr %data_pointer, i64 ${index}
|
|
113
|
+
%element = load %Pos, ptr %element_pointer
|
|
114
|
+
|
|
115
|
+
tail call void @sharePositive(%Pos %element)
|
|
116
|
+
tail call void @erasePositive(%Pos ${arr})
|
|
117
|
+
ret %Pos %element
|
|
67
118
|
"""
|
|
68
119
|
vm "array::unsafeGet[T](Array[T], Int)"
|
|
69
120
|
|
|
@@ -82,8 +133,15 @@ extern global def unsafeSet[T](arr: Array[T], index: Int, value: T): Unit =
|
|
|
82
133
|
js "array$set(${arr}, ${index}, ${value})"
|
|
83
134
|
chez "(begin (vector-set! ${arr} ${index} ${value}) #f)"
|
|
84
135
|
llvm """
|
|
85
|
-
%
|
|
86
|
-
|
|
136
|
+
%array_data = extractvalue %Pos ${arr}, 1
|
|
137
|
+
|
|
138
|
+
%data_pointer = getelementptr inbounds i64, ptr %array_data, i64 3
|
|
139
|
+
%element_pointer = getelementptr inbounds %Pos, ptr %data_pointer, i64 ${index}
|
|
140
|
+
%element = load %Pos, ptr %element_pointer, align 8
|
|
141
|
+
tail call void @erasePositive(%Pos %element)
|
|
142
|
+
store %Pos ${value}, ptr %element_pointer, align 8
|
|
143
|
+
tail call void @erasePositive(%Pos ${arr})
|
|
144
|
+
ret %Pos zeroinitializer
|
|
87
145
|
"""
|
|
88
146
|
vm "array::unsafeSet[T](Array[T], Int, T)"
|
|
89
147
|
|
|
@@ -3,7 +3,6 @@ module buffer
|
|
|
3
3
|
import array
|
|
4
4
|
|
|
5
5
|
// TODO (in Effekt compiler)
|
|
6
|
-
// - [ ] fix allocating into actually global region
|
|
7
6
|
// - [ ] fix exceptions on objects
|
|
8
7
|
// - [X] allow omitting braces after `at` for singleton regions
|
|
9
8
|
|
|
@@ -35,12 +34,12 @@ def emptyBuffer[T](capacity: Int): Buffer[T] at {global} = {
|
|
|
35
34
|
def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
|
|
36
35
|
// TODO allocate buffer (and array) into a region r.
|
|
37
36
|
val contents = array::allocate[T](initialCapacity)
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
val head = ref(0)
|
|
38
|
+
val tail = ref(0)
|
|
40
39
|
|
|
41
40
|
def size(): Int =
|
|
42
|
-
if (tail >= head) { tail - head }
|
|
43
|
-
else { initialCapacity - head + tail }
|
|
41
|
+
if (tail.get >= head.get) { tail.get - head.get }
|
|
42
|
+
else { initialCapacity - head.get + tail.get }
|
|
44
43
|
|
|
45
44
|
def capacity(): Int = initialCapacity - size()
|
|
46
45
|
|
|
@@ -51,36 +50,36 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
|
|
|
51
50
|
def read() = {
|
|
52
51
|
if (buffer.empty?) None()
|
|
53
52
|
else {
|
|
54
|
-
val result: T = contents.unsafeGet(head)
|
|
55
|
-
head
|
|
53
|
+
val result: T = contents.unsafeGet(head.get)
|
|
54
|
+
head.set(mod(head.get + 1, initialCapacity))
|
|
56
55
|
Some(result)
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
58
|
def write(el: T) = {
|
|
60
59
|
if (buffer.full?) <> // raise(BufferOverflow())
|
|
61
60
|
|
|
62
|
-
contents.unsafeSet(tail, el)
|
|
63
|
-
tail
|
|
61
|
+
contents.unsafeSet(tail.get, el)
|
|
62
|
+
tail.set(mod(tail.get + 1, initialCapacity))
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
buffer
|
|
67
66
|
}
|
|
68
67
|
def refBuffer[T](): Buffer[T] at {global} = {
|
|
69
|
-
|
|
68
|
+
val content = ref(None())
|
|
70
69
|
new Buffer[T] {
|
|
71
|
-
def capacity() = if (content.isEmpty) 1 else 0
|
|
72
|
-
def full?() = content.isDefined
|
|
73
|
-
def empty?() = isEmpty(content)
|
|
70
|
+
def capacity() = if (content.get.isEmpty) 1 else 0
|
|
71
|
+
def full?() = content.get.isDefined
|
|
72
|
+
def empty?() = isEmpty(content.get)
|
|
74
73
|
def read() = {
|
|
75
|
-
val res = content
|
|
76
|
-
content
|
|
74
|
+
val res = content.get
|
|
75
|
+
content.set(None())
|
|
77
76
|
res
|
|
78
77
|
}
|
|
79
|
-
def write(el: T) = content match {
|
|
78
|
+
def write(el: T) = content.get match {
|
|
80
79
|
case Some(v) =>
|
|
81
80
|
<> // do raise(BufferOverflow(), "Cannot read element from buffer")
|
|
82
81
|
case None() =>
|
|
83
|
-
content
|
|
82
|
+
content.set(Some(el))
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
}
|
|
@@ -88,24 +87,24 @@ def refBuffer[T](): Buffer[T] at {global} = {
|
|
|
88
87
|
namespace examples {
|
|
89
88
|
def main() = ignore[BufferOverflow] {
|
|
90
89
|
// Buffer with capacity 1
|
|
91
|
-
def b = emptyBuffer[Int](1)
|
|
92
|
-
println(b.capacity)
|
|
93
|
-
println(b.full?)
|
|
90
|
+
def b = emptyBuffer[Int](1)
|
|
91
|
+
println(b.capacity)
|
|
92
|
+
println(b.full?)
|
|
94
93
|
|
|
95
|
-
b.write(17)
|
|
96
|
-
println(b.read())
|
|
94
|
+
b.write(17)
|
|
95
|
+
println(b.read())
|
|
97
96
|
|
|
98
97
|
// buffer with capacity 3
|
|
99
|
-
def ringbuffer = emptyBuffer[Int](3)
|
|
100
|
-
ringbuffer.write(1)
|
|
101
|
-
ringbuffer.write(2)
|
|
102
|
-
println(ringbuffer.read())
|
|
103
|
-
ringbuffer.write(3)
|
|
104
|
-
println(ringbuffer.read())
|
|
105
|
-
println(ringbuffer.read())
|
|
106
|
-
ringbuffer.write(4)
|
|
107
|
-
ringbuffer.write(5)
|
|
108
|
-
println(ringbuffer.read())
|
|
109
|
-
println(ringbuffer.read())
|
|
98
|
+
def ringbuffer = emptyBuffer[Int](3)
|
|
99
|
+
ringbuffer.write(1)
|
|
100
|
+
ringbuffer.write(2)
|
|
101
|
+
println(ringbuffer.read())
|
|
102
|
+
ringbuffer.write(3)
|
|
103
|
+
println(ringbuffer.read())
|
|
104
|
+
println(ringbuffer.read())
|
|
105
|
+
ringbuffer.write(4)
|
|
106
|
+
ringbuffer.write(5)
|
|
107
|
+
println(ringbuffer.read())
|
|
108
|
+
println(ringbuffer.read())
|
|
110
109
|
}
|
|
111
110
|
}
|
|
@@ -2,6 +2,17 @@ module bytearray
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A memory managed, mutable, fixed-length array of bytes.
|
|
5
|
+
*
|
|
6
|
+
* We represent bytearrays like positive types.
|
|
7
|
+
*
|
|
8
|
+
* - The field `tag` contains the size
|
|
9
|
+
* - The field `obj` points to memory with the following layout:
|
|
10
|
+
*
|
|
11
|
+
* +--[ Header ]--+--------------+
|
|
12
|
+
* | Rc | Eraser | Contents ... |
|
|
13
|
+
* +--------------+--------------+
|
|
14
|
+
*
|
|
15
|
+
* The eraser does nothing.
|
|
5
16
|
*/
|
|
6
17
|
extern type ByteArray
|
|
7
18
|
// = llvm "%Pos"
|
|
@@ -12,32 +23,47 @@ extern type ByteArray
|
|
|
12
23
|
extern global def allocate(size: Int): ByteArray =
|
|
13
24
|
js "(new Uint8Array(${size}))"
|
|
14
25
|
llvm """
|
|
15
|
-
%
|
|
16
|
-
|
|
26
|
+
%object_size = add i64 ${size}, 16
|
|
27
|
+
%object_alloc = tail call noalias ptr @malloc(i64 noundef %object_size)
|
|
28
|
+
store i64 0, ptr %object_alloc, align 8
|
|
29
|
+
%object_data_ptr = getelementptr inbounds i8, ptr %object_alloc, i64 8
|
|
30
|
+
store ptr @bytearray_erase_noop, ptr %object_data_ptr, align 8
|
|
31
|
+
%ret_object1 = insertvalue %Pos poison, i64 ${size}, 0
|
|
32
|
+
%ret_object2 = insertvalue %Pos %ret_object1, ptr %object_alloc, 1
|
|
33
|
+
ret %Pos %ret_object2
|
|
17
34
|
"""
|
|
18
35
|
chez "(make-bytevector ${size})"
|
|
19
36
|
|
|
20
37
|
extern pure def size(arr: ByteArray): Int =
|
|
21
38
|
js "${arr}.length"
|
|
22
39
|
llvm """
|
|
23
|
-
%size =
|
|
24
|
-
|
|
40
|
+
%size = extractvalue %Pos ${arr}, 0
|
|
41
|
+
tail call void @erasePositive(%Pos ${arr})
|
|
42
|
+
ret i64 %size
|
|
25
43
|
"""
|
|
26
44
|
chez "(bytevector-length ${arr})"
|
|
27
45
|
|
|
28
46
|
extern global def unsafeGet(arr: ByteArray, index: Int): Byte =
|
|
29
47
|
js "(${arr})[${index}]"
|
|
30
48
|
llvm """
|
|
31
|
-
%
|
|
32
|
-
|
|
49
|
+
%arr_ptr = extractvalue %Pos ${arr}, 1
|
|
50
|
+
%arr_data_ptr = getelementptr inbounds i8, ptr %arr_ptr, i64 16
|
|
51
|
+
%element_ptr = getelementptr inbounds i8, ptr %arr_data_ptr, i64 ${index}
|
|
52
|
+
%element = load i8, ptr %element_ptr, align 1
|
|
53
|
+
tail call void @erasePositive(%Pos ${arr})
|
|
54
|
+
ret i8 %element
|
|
33
55
|
"""
|
|
34
56
|
chez "(bytevector-u8-ref ${arr} ${index})"
|
|
35
57
|
|
|
36
58
|
extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
|
|
37
59
|
js "bytearray$set(${arr}, ${index}, ${value})"
|
|
38
60
|
llvm """
|
|
39
|
-
%
|
|
40
|
-
|
|
61
|
+
%arr_ptr = extractvalue %Pos ${arr}, 1
|
|
62
|
+
%arr_data_ptr = getelementptr inbounds i8, ptr %arr_ptr, i64 16
|
|
63
|
+
%element_ptr = getelementptr inbounds i8, ptr %arr_data_ptr, i64 ${index}
|
|
64
|
+
store i8 ${value}, ptr %element_ptr, align 1
|
|
65
|
+
tail call void @erasePositive(%Pos ${arr})
|
|
66
|
+
ret %Pos zeroinitializer
|
|
41
67
|
"""
|
|
42
68
|
chez "(bytevector-u8-set! ${arr} ${index} ${value})"
|
|
43
69
|
|
|
@@ -121,6 +147,12 @@ extern js """
|
|
|
121
147
|
}
|
|
122
148
|
"""
|
|
123
149
|
|
|
150
|
+
extern llvm """
|
|
151
|
+
define void @bytearray_erase_noop(ptr readnone %0) {
|
|
152
|
+
ret void
|
|
153
|
+
}
|
|
154
|
+
"""
|
|
155
|
+
|
|
124
156
|
extern chez """
|
|
125
157
|
(define (bytearray$compare b1 b2)
|
|
126
158
|
(let ([len1 (bytevector-length b1)]
|
|
@@ -25,82 +25,82 @@ def emptyQueue[T](): Queue[T] at {global} =
|
|
|
25
25
|
|
|
26
26
|
def emptyQueue[T](initialCapacity: Int): Queue[T] at {global} = {
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
val contents = ref(array[Option[T]](initialCapacity, None()))
|
|
29
|
+
val head = ref(0)
|
|
30
|
+
val tail = ref(0)
|
|
31
|
+
val size = ref(0)
|
|
32
|
+
val capacity = ref(initialCapacity)
|
|
33
33
|
|
|
34
34
|
def remove(arr: Array[Option[T]], index: Int): Option[T] = {
|
|
35
|
-
with on[OutOfBounds].default { None() }
|
|
36
|
-
val value = arr.get(index)
|
|
37
|
-
arr.set(index, None())
|
|
35
|
+
with on[OutOfBounds].default { None() }
|
|
36
|
+
val value = arr.get(index)
|
|
37
|
+
arr.set(index, None())
|
|
38
38
|
value
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
def nonEmpty[T] { p: => Option[T] / Exception[OutOfBounds] }: Option[T] =
|
|
42
|
-
if (size <= 0) None() else on[OutOfBounds].default { None() } { p() }
|
|
42
|
+
if (size.get <= 0) None() else on[OutOfBounds].default { None() } { p() }
|
|
43
43
|
|
|
44
44
|
// Exponential back-off
|
|
45
45
|
def resizeTo(requiredSize: Int): Unit =
|
|
46
|
-
if (requiredSize <= capacity) () else {
|
|
46
|
+
if (requiredSize <= capacity.get) () else {
|
|
47
47
|
with on[OutOfBounds].ignore // should not happen
|
|
48
48
|
|
|
49
|
-
val oldSize = capacity
|
|
50
|
-
val newSize = capacity * 2
|
|
51
|
-
val oldContents = contents
|
|
49
|
+
val oldSize = capacity.get
|
|
50
|
+
val newSize = capacity.get * 2
|
|
51
|
+
val oldContents = contents.get
|
|
52
52
|
val newContents = array::allocate[Option[T]](newSize)
|
|
53
53
|
|
|
54
|
-
if (head < tail) {
|
|
54
|
+
if (head.get < tail.get) {
|
|
55
55
|
// The queue does not wrap around; direct copy is possible.
|
|
56
|
-
copy(oldContents, head, newContents, 0, size) // changed tail to size
|
|
57
|
-
} else if (size > 0) {
|
|
56
|
+
copy(oldContents, head.get, newContents, 0, size.get) // changed tail to size
|
|
57
|
+
} else if (size.get > 0) {
|
|
58
58
|
// The queue wraps around; copy in two segments.
|
|
59
|
-
copy(oldContents, head, newContents, 0, oldSize - head) // changed oldSize to oldSize - head
|
|
60
|
-
copy(oldContents, 0, newContents, oldSize - head, tail) // changed oldSize - head to oldSize - head
|
|
59
|
+
copy(oldContents, head.get, newContents, 0, oldSize - head.get) // changed oldSize to oldSize - head
|
|
60
|
+
copy(oldContents, 0, newContents, oldSize - head.get, tail.get) // changed oldSize - head to oldSize - head
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
contents
|
|
64
|
-
capacity
|
|
65
|
-
head
|
|
66
|
-
tail
|
|
63
|
+
contents.set(newContents)
|
|
64
|
+
capacity.set(newSize)
|
|
65
|
+
head.set(0)
|
|
66
|
+
tail.set(oldSize)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
def queue = new Queue[T] {
|
|
70
|
-
def empty?() = size <= 0
|
|
70
|
+
def empty?() = size.get <= 0
|
|
71
71
|
|
|
72
72
|
def popFront() =
|
|
73
73
|
nonEmpty {
|
|
74
|
-
val result = contents.remove(head)
|
|
75
|
-
head
|
|
76
|
-
size
|
|
74
|
+
val result = contents.get.remove(head.get)
|
|
75
|
+
head.set(mod(head.get + 1, capacity.get))
|
|
76
|
+
size.set(size.get - 1)
|
|
77
77
|
result
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
def popBack() =
|
|
81
81
|
nonEmpty {
|
|
82
|
-
tail
|
|
83
|
-
val result = contents.remove(tail)
|
|
84
|
-
size
|
|
82
|
+
tail.set(mod(tail.get - 1 + capacity.get, capacity.get))
|
|
83
|
+
val result = contents.get.remove(tail.get)
|
|
84
|
+
size.set(size.get - 1)
|
|
85
85
|
result
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
def peekFront() = nonEmpty { contents.get(head) }
|
|
88
|
+
def peekFront() = nonEmpty { contents.get.get(head.get) }
|
|
89
89
|
|
|
90
|
-
def peekBack() = nonEmpty { contents.get(tail) }
|
|
90
|
+
def peekBack() = nonEmpty { contents.get.get(tail.get) }
|
|
91
91
|
|
|
92
92
|
def pushFront(el: T) = {
|
|
93
|
-
resizeTo(size + 1)
|
|
94
|
-
head
|
|
95
|
-
size
|
|
96
|
-
contents.unsafeSet(head, Some(el))
|
|
93
|
+
resizeTo(size.get + 1)
|
|
94
|
+
head.set(mod(head.get - 1 + capacity.get, capacity.get))
|
|
95
|
+
size.set(size.get + 1)
|
|
96
|
+
contents.get.unsafeSet(head.get, Some(el))
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
def pushBack(el: T) = {
|
|
100
|
-
resizeTo(size + 1)
|
|
101
|
-
contents.unsafeSet(tail, Some(el))
|
|
102
|
-
size
|
|
103
|
-
tail
|
|
100
|
+
resizeTo(size.get + 1)
|
|
101
|
+
contents.get.unsafeSet(tail.get, Some(el))
|
|
102
|
+
size.set(size.get + 1)
|
|
103
|
+
tail.set(mod(tail.get + 1, capacity.get))
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
queue
|
|
@@ -109,30 +109,30 @@ def emptyQueue[T](initialCapacity: Int): Queue[T] at {global} = {
|
|
|
109
109
|
namespace examples {
|
|
110
110
|
def main() = {
|
|
111
111
|
// queue with initial capacity 4
|
|
112
|
-
def b = emptyQueue[Int](4)
|
|
113
|
-
println(b.empty?)
|
|
114
|
-
b.pushFront(1)
|
|
115
|
-
b.pushBack(2)
|
|
116
|
-
b.pushFront(3)
|
|
117
|
-
b.pushBack(4)
|
|
112
|
+
def b = emptyQueue[Int](4)
|
|
113
|
+
println(b.empty?)
|
|
114
|
+
b.pushFront(1)
|
|
115
|
+
b.pushBack(2)
|
|
116
|
+
b.pushFront(3)
|
|
117
|
+
b.pushBack(4)
|
|
118
118
|
// this will cause resizing:
|
|
119
|
-
b.pushBack(5)
|
|
120
|
-
b.pushBack(6)
|
|
121
|
-
b.pushBack(7)
|
|
122
|
-
b.pushBack(8)
|
|
119
|
+
b.pushBack(5)
|
|
120
|
+
b.pushBack(6)
|
|
121
|
+
b.pushBack(7)
|
|
122
|
+
b.pushBack(8)
|
|
123
123
|
// and again:
|
|
124
|
-
b.pushBack(9)
|
|
125
|
-
|
|
126
|
-
println(b.empty?)
|
|
127
|
-
println(b.popFront())
|
|
128
|
-
println(b.popFront())
|
|
129
|
-
println(b.popFront())
|
|
130
|
-
println(b.popFront())
|
|
131
|
-
println(b.popFront())
|
|
132
|
-
println(b.popFront())
|
|
133
|
-
println(b.popFront())
|
|
134
|
-
println(b.popFront())
|
|
135
|
-
println(b.popFront())
|
|
136
|
-
println(b.popFront())
|
|
124
|
+
b.pushBack(9)
|
|
125
|
+
|
|
126
|
+
println(b.empty?)
|
|
127
|
+
println(b.popFront()) // Some(3)
|
|
128
|
+
println(b.popFront()) // Some(1)
|
|
129
|
+
println(b.popFront()) // Some(2)
|
|
130
|
+
println(b.popFront()) // Some(4)
|
|
131
|
+
println(b.popFront()) // Some(5)
|
|
132
|
+
println(b.popFront()) // Some(6)
|
|
133
|
+
println(b.popFront()) // Some(7)
|
|
134
|
+
println(b.popFront()) // Some(8)
|
|
135
|
+
println(b.popFront()) // Some(9)
|
|
136
|
+
println(b.popFront()) // None()
|
|
137
137
|
}
|
|
138
138
|
}
|
|
@@ -9,17 +9,44 @@ extern js """
|
|
|
9
9
|
}
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
+
extern llvm """
|
|
13
|
+
define void @c_ref_erase_field(ptr %0) {
|
|
14
|
+
%field = load %Pos, ptr %0, align 8
|
|
15
|
+
tail call void @erasePositive(%Pos %field)
|
|
16
|
+
ret void
|
|
17
|
+
}
|
|
18
|
+
"""
|
|
19
|
+
|
|
12
20
|
/// Global, mutable references
|
|
13
21
|
extern type Ref[T]
|
|
14
22
|
|
|
23
|
+
/** We represent references like positive types in LLVM
|
|
24
|
+
* The tag is 0 and the obj points to memory with the following layout:
|
|
25
|
+
*
|
|
26
|
+
* +--[ Header ]--------------+------------+
|
|
27
|
+
* | ReferenceCount | Eraser | Field |
|
|
28
|
+
* +--------------------------+------------+
|
|
29
|
+
*/
|
|
30
|
+
|
|
15
31
|
/// Allocates a new reference, keeping its value _undefined_.
|
|
16
32
|
/// Prefer using `ref` constructor instead to ensure that the value is defined.
|
|
17
33
|
extern global def allocate[T](): Ref[T] =
|
|
18
34
|
js "{ value: undefined }"
|
|
19
35
|
chez "(box #f)"
|
|
20
36
|
llvm """
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
; sizeof Header + sizeof Pos = 32
|
|
38
|
+
%ref = tail call noalias ptr @malloc(i64 noundef 32)
|
|
39
|
+
%refEraser = getelementptr ptr, ptr %ref, i64 1
|
|
40
|
+
%fieldTag = getelementptr ptr, ptr %ref, i64 2
|
|
41
|
+
%fieldData_pointer = getelementptr ptr, ptr %ref, i64 3
|
|
42
|
+
|
|
43
|
+
store i64 0, ptr %ref, align 8
|
|
44
|
+
store ptr @c_ref_erase_field, ptr %refEraser, align 8
|
|
45
|
+
store i64 0, ptr %fieldTag, align 8
|
|
46
|
+
store ptr null, ptr %fieldData_pointer, align 8
|
|
47
|
+
|
|
48
|
+
%refWrap = insertvalue %Pos { i64 0, ptr poison }, ptr %ref, 1
|
|
49
|
+
ret %Pos %refWrap
|
|
23
50
|
"""
|
|
24
51
|
|
|
25
52
|
/// Creates a new reference with the initial value `init`.
|
|
@@ -27,8 +54,20 @@ extern global def ref[T](init: T): Ref[T] =
|
|
|
27
54
|
js "{ value: ${init} }"
|
|
28
55
|
chez "(box ${init})"
|
|
29
56
|
llvm """
|
|
30
|
-
%
|
|
31
|
-
|
|
57
|
+
%initTag = extractvalue %Pos ${init}, 0
|
|
58
|
+
%initObject_pointer = extractvalue %Pos ${init}, 1
|
|
59
|
+
; sizeof Header + sizeof Pos = 32
|
|
60
|
+
%ref = tail call noalias ptr @malloc(i64 noundef 32)
|
|
61
|
+
%refEraser = getelementptr ptr, ptr %ref, i64 1
|
|
62
|
+
%refField = getelementptr ptr, ptr %ref, i64 2
|
|
63
|
+
|
|
64
|
+
store i64 0, ptr %ref, align 8
|
|
65
|
+
store ptr @c_ref_erase_field, ptr %refEraser, align 8
|
|
66
|
+
store %Pos ${init}, ptr %refField, align 8
|
|
67
|
+
|
|
68
|
+
%refWrap = insertvalue %Pos { i64 0, ptr poison }, ptr %ref, 1
|
|
69
|
+
|
|
70
|
+
ret %Pos %refWrap
|
|
32
71
|
"""
|
|
33
72
|
vm "ref::ref[T](T)"
|
|
34
73
|
|
|
@@ -36,9 +75,15 @@ extern global def ref[T](init: T): Ref[T] =
|
|
|
36
75
|
extern global def get[T](ref: Ref[T]): T =
|
|
37
76
|
js "${ref}.value"
|
|
38
77
|
chez "(unbox ${ref})"
|
|
39
|
-
|
|
40
|
-
%
|
|
41
|
-
|
|
78
|
+
llvm """
|
|
79
|
+
%ref_pointer = extractvalue %Pos ${ref}, 1
|
|
80
|
+
%refField_pointer = getelementptr inbounds ptr, ptr %ref_pointer, i64 2
|
|
81
|
+
%refField = load %Pos, ptr %refField_pointer, align 8
|
|
82
|
+
|
|
83
|
+
tail call void @sharePositive(%Pos %refField)
|
|
84
|
+
tail call void @erasePositive(%Pos ${ref})
|
|
85
|
+
|
|
86
|
+
ret %Pos %refField
|
|
42
87
|
"""
|
|
43
88
|
vm "ref::get[T](Ref[T])"
|
|
44
89
|
|
|
@@ -47,7 +92,15 @@ extern global def set[T](ref: Ref[T], value: T): Unit =
|
|
|
47
92
|
js "set$impl(${ref}, ${value})"
|
|
48
93
|
chez "(set-box! ${ref} ${value})"
|
|
49
94
|
llvm """
|
|
50
|
-
%
|
|
51
|
-
|
|
95
|
+
%refField_pointer = extractvalue %Pos ${ref}, 1
|
|
96
|
+
|
|
97
|
+
%field_pointer = getelementptr ptr, ptr %refField_pointer, i64 2
|
|
98
|
+
%field = load %Pos, ptr %field_pointer, align 8
|
|
99
|
+
|
|
100
|
+
tail call void @erasePositive(%Pos %field)
|
|
101
|
+
store %Pos ${value}, ptr %field_pointer
|
|
102
|
+
tail call void @erasePositive(%Pos ${ref})
|
|
103
|
+
|
|
104
|
+
ret %Pos zeroinitializer
|
|
52
105
|
"""
|
|
53
106
|
vm "ref::set[T](Ref[T], T)"
|
|
@@ -14,7 +14,7 @@ def stringBuffer[A] { prog: => A / StringBuffer }: A = {
|
|
|
14
14
|
var pos = 0
|
|
15
15
|
|
|
16
16
|
def ensureCapacity(sizeToAdd: Int): Unit = {
|
|
17
|
-
val cap = buffer.size - pos
|
|
17
|
+
val cap = buffer.size - pos
|
|
18
18
|
if (sizeToAdd <= cap) ()
|
|
19
19
|
else {
|
|
20
20
|
// Double the capacity while ensuring the required capacity
|