@effekt-lang/effekt 0.78.0 → 0.79.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 CHANGED
Binary file
@@ -108,7 +108,7 @@ define void @assertInbounds(i64 %index, %Pos %arr) alwaysinline {
108
108
  %in_bounds = icmp ult i64 %index, %size
109
109
  br i1 %in_bounds, label %ok, label %out
110
110
  out:
111
- call void @exit(i64 1)
111
+ call void @exit(i32 1)
112
112
  unreachable
113
113
  ok:
114
114
  ret void
@@ -61,7 +61,7 @@ define void @assertInboundsByteArray(i64 %index, %Pos %arr) alwaysinline {
61
61
  %in_bounds = icmp ult i64 %index, %size
62
62
  br i1 %in_bounds, label %ok, label %out
63
63
  out:
64
- call void @exit(i64 1)
64
+ call void @exit(i32 1)
65
65
  unreachable
66
66
  ok:
67
67
  ret void
@@ -6,14 +6,40 @@ import bytearray
6
6
  import stream
7
7
 
8
8
  /// Internal trie node type.
9
+ ///
10
+ /// Nodes with at most four children are `Branch1` ... `Branch4`, holding their
11
+ /// children behind `Ref`s so that descending along an existing path can
12
+ /// update in place without rebuilding the spine. A fifth child promotes the
13
+ /// node to the dense representation (`Branch256` / `Node256`), a full 256-slot
14
+ /// array indexed by the byte, whose cells are mutable themselves.
15
+ ///
16
+ /// Invariant: in the sparse representations, `byte1 < byte2 < byte3 < byte4`
17
+ /// (bytes are unsigned), so iteration is in key order in every representation.
18
+ ///
19
+ /// `Leaf`, `Node1` ... `Node4` and `Node256` carry a value at this
20
+ /// position; `Branch1` ... `Branch4` and `Branch256` do not.
21
+ ///
22
+ /// No `Trie` value is ever shared between two live positions: insertion
23
+ /// mutates through `Ref`s and array cells, so aliasing a subtree would be
24
+ /// unsound.
9
25
  type Trie[A] {
10
26
  Empty()
11
27
  Leaf(value: A)
12
- Branch(children: Array[Trie[A]])
13
- Node(value: A, children: Array[Trie[A]])
28
+ Branch1(byte: Byte, child: Ref[Trie[A]])
29
+ Node1(value: A, byte: Byte, child: Ref[Trie[A]])
30
+ Branch2(byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]])
31
+ Node2(value: A, byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]])
32
+ Branch3(byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]])
33
+ Node3(value: A, byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]])
34
+ Branch4(byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]], byte4: Byte, child4: Ref[Trie[A]])
35
+ Node4(value: A, byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]], byte4: Byte, child4: Ref[Trie[A]])
36
+ Branch256(children: Array[Trie[A]])
37
+ Node256(value: A, children: Array[Trie[A]])
14
38
  }
15
39
 
16
40
  /// A mutable trie-based dictionary for efficient key-value storage with bytearray keys.
41
+ ///
42
+ /// Mutating the dictionary while iterating it (`each`, `eachTagged`) is undefined.
17
43
  type Dictionary[A] = Ref[Trie[A]]
18
44
 
19
45
  /// A record pairing a key with a value for use in streams.
@@ -27,34 +53,104 @@ def insert[A](dictionary: Dictionary[A], key: String, value: A): Unit =
27
53
 
28
54
  /// Insert a key-value pair into the dictionary with a ByteArray key.
29
55
  def insert[A](dictionary: Dictionary[A], key: ByteArray, value: A): Unit = {
56
+ val n = key.size
57
+
58
+ // The rest of the key from position `i` on, as a chain of `Branch1`s
59
+ // ending in a `Leaf`.
60
+ def chain(i: Int): Trie[A] =
61
+ if (i < n) Branch1(key.get(i), ref(chain(i + 1)))
62
+ else Leaf(value)
63
+
64
+ // The four children of a `Branch4`, plus the rest of the key at `b`,
65
+ // as a full 256-slot array.
66
+ def densified(byte1: Byte, child1: Ref[Trie[A]], byte2: Byte, child2: Ref[Trie[A]], byte3: Byte, child3: Ref[Trie[A]], byte4: Byte, child4: Ref[Trie[A]], b: Byte, i: Int): Array[Trie[A]] = {
67
+ val children = array[Trie[A]](256, Empty())
68
+ children.set(byte1.toInt, child1.get)
69
+ children.set(byte2.toInt, child2.get)
70
+ children.set(byte3.toInt, child3.get)
71
+ children.set(byte4.toInt, child4.get)
72
+ children.set(b.toInt, chain(i + 1))
73
+ children
74
+ }
75
+
30
76
  def go(trie: Trie[A], i: Int): Trie[A] =
31
- if (i < key.size) {
77
+ if (i < n) {
78
+ val b = key.get(i)
32
79
  trie match {
33
- case Empty() =>
34
- val child = go(Empty(), i + 1)
35
- val children = array(256, Empty())
36
- children.set(key.get(i).toInt, child)
37
- Branch(children)
38
- case Leaf(existing) =>
39
- val child = go(Empty(), i + 1)
40
- val children = array(256, Empty())
41
- children.set(key.get(i).toInt, child)
42
- Node(existing, children)
43
- case Branch(children) =>
44
- val child = go(children.get(key.get(i).toInt), i + 1)
45
- children.set(key.get(i).toInt, child)
46
- Branch(children)
47
- case Node(existing, children) =>
48
- val child = go(children.get(key.get(i).toInt), i + 1)
49
- children.set(key.get(i).toInt, child)
50
- Node(existing, children)
80
+ case Empty() => chain(i)
81
+ case Leaf(existing) => Node1(existing, b, ref(chain(i + 1)))
82
+
83
+ case Branch1(byte1, child1) =>
84
+ if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
85
+ else if (b.toInt < byte1.toInt) Branch2(b, ref(chain(i + 1)), byte1, child1)
86
+ else Branch2(byte1, child1, b, ref(chain(i + 1)))
87
+ case Node1(existing, byte1, child1) =>
88
+ if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
89
+ else if (b.toInt < byte1.toInt) Node2(existing, b, ref(chain(i + 1)), byte1, child1)
90
+ else Node2(existing, byte1, child1, b, ref(chain(i + 1)))
91
+
92
+ case Branch2(byte1, child1, byte2, child2) =>
93
+ if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
94
+ else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
95
+ else if (b.toInt < byte1.toInt) Branch3(b, ref(chain(i + 1)), byte1, child1, byte2, child2)
96
+ else if (b.toInt < byte2.toInt) Branch3(byte1, child1, b, ref(chain(i + 1)), byte2, child2)
97
+ else Branch3(byte1, child1, byte2, child2, b, ref(chain(i + 1)))
98
+ case Node2(existing, byte1, child1, byte2, child2) =>
99
+ if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
100
+ else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
101
+ else if (b.toInt < byte1.toInt) Node3(existing, b, ref(chain(i + 1)), byte1, child1, byte2, child2)
102
+ else if (b.toInt < byte2.toInt) Node3(existing, byte1, child1, b, ref(chain(i + 1)), byte2, child2)
103
+ else Node3(existing, byte1, child1, byte2, child2, b, ref(chain(i + 1)))
104
+
105
+ case Branch3(byte1, child1, byte2, child2, byte3, child3) =>
106
+ if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
107
+ else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
108
+ else if (b == byte3) { child3.set(go(child3.get, i + 1)); trie }
109
+ else if (b.toInt < byte1.toInt) Branch4(b, ref(chain(i + 1)), byte1, child1, byte2, child2, byte3, child3)
110
+ else if (b.toInt < byte2.toInt) Branch4(byte1, child1, b, ref(chain(i + 1)), byte2, child2, byte3, child3)
111
+ else if (b.toInt < byte3.toInt) Branch4(byte1, child1, byte2, child2, b, ref(chain(i + 1)), byte3, child3)
112
+ else Branch4(byte1, child1, byte2, child2, byte3, child3, b, ref(chain(i + 1)))
113
+ case Node3(existing, byte1, child1, byte2, child2, byte3, child3) =>
114
+ if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
115
+ else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
116
+ else if (b == byte3) { child3.set(go(child3.get, i + 1)); trie }
117
+ else if (b.toInt < byte1.toInt) Node4(existing, b, ref(chain(i + 1)), byte1, child1, byte2, child2, byte3, child3)
118
+ else if (b.toInt < byte2.toInt) Node4(existing, byte1, child1, b, ref(chain(i + 1)), byte2, child2, byte3, child3)
119
+ else if (b.toInt < byte3.toInt) Node4(existing, byte1, child1, byte2, child2, b, ref(chain(i + 1)), byte3, child3)
120
+ else Node4(existing, byte1, child1, byte2, child2, byte3, child3, b, ref(chain(i + 1)))
121
+
122
+ case Branch4(byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
123
+ if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
124
+ else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
125
+ else if (b == byte3) { child3.set(go(child3.get, i + 1)); trie }
126
+ else if (b == byte4) { child4.set(go(child4.get, i + 1)); trie }
127
+ else Branch256(densified(byte1, child1, byte2, child2, byte3, child3, byte4, child4, b, i))
128
+ case Node4(existing, byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
129
+ if (b == byte1) { child1.set(go(child1.get, i + 1)); trie }
130
+ else if (b == byte2) { child2.set(go(child2.get, i + 1)); trie }
131
+ else if (b == byte3) { child3.set(go(child3.get, i + 1)); trie }
132
+ else if (b == byte4) { child4.set(go(child4.get, i + 1)); trie }
133
+ else Node256(existing, densified(byte1, child1, byte2, child2, byte3, child3, byte4, child4, b, i))
134
+
135
+ case Branch256(children) =>
136
+ children.set(b.toInt, go(children.get(b.toInt), i + 1)); trie
137
+ case Node256(_, children) =>
138
+ children.set(b.toInt, go(children.get(b.toInt), i + 1)); trie
51
139
  }
52
140
  } else {
53
141
  trie match {
54
142
  case Empty() => Leaf(value)
55
143
  case Leaf(_) => Leaf(value)
56
- case Node(_, children) => Node(value, children)
57
- case Branch(children) => Node(value, children)
144
+ case Branch1(byte1, child1) => Node1(value, byte1, child1)
145
+ case Node1(_, byte1, child1) => Node1(value, byte1, child1)
146
+ case Branch2(byte1, child1, byte2, child2) => Node2(value, byte1, child1, byte2, child2)
147
+ case Node2(_, byte1, child1, byte2, child2) => Node2(value, byte1, child1, byte2, child2)
148
+ case Branch3(byte1, child1, byte2, child2, byte3, child3) => Node3(value, byte1, child1, byte2, child2, byte3, child3)
149
+ case Node3(_, byte1, child1, byte2, child2, byte3, child3) => Node3(value, byte1, child1, byte2, child2, byte3, child3)
150
+ case Branch4(byte1, child1, byte2, child2, byte3, child3, byte4, child4) => Node4(value, byte1, child1, byte2, child2, byte3, child3, byte4, child4)
151
+ case Node4(_, byte1, child1, byte2, child2, byte3, child3, byte4, child4) => Node4(value, byte1, child1, byte2, child2, byte3, child3, byte4, child4)
152
+ case Branch256(children) => Node256(value, children)
153
+ case Node256(_, children) => Node256(value, children)
58
154
  }
59
155
  }
60
156
  dictionary.set(go(dictionary.get, 0))
@@ -68,18 +164,56 @@ def lookup[A](dictionary: Dictionary[A], key: String): Option[A] =
68
164
  def lookup[A](dictionary: Dictionary[A], key: ByteArray): Option[A] = {
69
165
  def go(trie: Trie[A], i: Int): Option[A] =
70
166
  if (i < key.size) {
167
+ val b = key.get(i)
71
168
  trie match {
72
169
  case Empty() => None()
73
170
  case Leaf(_) => None()
74
- case Branch(children) => go(children.get(key.get(i).toInt), i + 1)
75
- case Node(_, children) => go(children.get(key.get(i).toInt), i + 1)
171
+ case Branch1(byte1, child1) =>
172
+ if (b == byte1) go(child1.get, i + 1) else None()
173
+ case Node1(_, byte1, child1) =>
174
+ if (b == byte1) go(child1.get, i + 1) else None()
175
+ case Branch2(byte1, child1, byte2, child2) =>
176
+ if (b == byte1) go(child1.get, i + 1)
177
+ else if (b == byte2) go(child2.get, i + 1)
178
+ else None()
179
+ case Node2(_, byte1, child1, byte2, child2) =>
180
+ if (b == byte1) go(child1.get, i + 1)
181
+ else if (b == byte2) go(child2.get, i + 1)
182
+ else None()
183
+ case Branch3(byte1, child1, byte2, child2, byte3, child3) =>
184
+ if (b == byte1) go(child1.get, i + 1)
185
+ else if (b == byte2) go(child2.get, i + 1)
186
+ else if (b == byte3) go(child3.get, i + 1)
187
+ else None()
188
+ case Node3(_, byte1, child1, byte2, child2, byte3, child3) =>
189
+ if (b == byte1) go(child1.get, i + 1)
190
+ else if (b == byte2) go(child2.get, i + 1)
191
+ else if (b == byte3) go(child3.get, i + 1)
192
+ else None()
193
+ case Branch4(byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
194
+ if (b == byte1) go(child1.get, i + 1)
195
+ else if (b == byte2) go(child2.get, i + 1)
196
+ else if (b == byte3) go(child3.get, i + 1)
197
+ else if (b == byte4) go(child4.get, i + 1)
198
+ else None()
199
+ case Node4(_, byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
200
+ if (b == byte1) go(child1.get, i + 1)
201
+ else if (b == byte2) go(child2.get, i + 1)
202
+ else if (b == byte3) go(child3.get, i + 1)
203
+ else if (b == byte4) go(child4.get, i + 1)
204
+ else None()
205
+ case Branch256(children) => go(children.get(b.toInt), i + 1)
206
+ case Node256(_, children) => go(children.get(b.toInt), i + 1)
76
207
  }
77
208
  } else {
78
209
  trie match {
79
- case Empty() => None()
80
210
  case Leaf(value) => Some(value)
81
- case Branch(_) => None()
82
- case Node(value, _) => Some(value)
211
+ case Node1(value, _, _) => Some(value)
212
+ case Node2(value, _, _, _, _) => Some(value)
213
+ case Node3(value, _, _, _, _, _, _) => Some(value)
214
+ case Node4(value, _, _, _, _, _, _, _, _) => Some(value)
215
+ case Node256(value, _) => Some(value)
216
+ case _ => None()
83
217
  }
84
218
  }
85
219
  go(dictionary.get, 0)
@@ -87,48 +221,104 @@ def lookup[A](dictionary: Dictionary[A], key: ByteArray): Option[A] = {
87
221
 
88
222
  /// Collect key-value pairs from a stream of Tagged elements into a fresh Dictionary.
89
223
  def collect[A] { stream: () => Unit / emit[Tagged[A]] }: Dictionary[A] = {
90
- val dictionary = ref(Empty())
224
+ val dictionary = empty[A]()
91
225
  for[Tagged[A]] { stream() } { case Tagged(bytes, value) =>
92
226
  dictionary.insert(bytes, value)
93
227
  }
94
- return dictionary
228
+ dictionary
95
229
  }
96
230
 
97
- /// Emit all values stored in the dictionary as a push stream.
231
+ /// Emit all values stored in the dictionary as a push stream,
232
+ /// in lexicographic key order (a key before its extensions).
98
233
  def each[A](dictionary: Dictionary[A]): Unit / emit[A] = {
99
234
  def go(trie: Trie[A]): Unit / emit[A] =
100
235
  trie match {
101
236
  case Empty() => ()
102
237
  case Leaf(value) => do emit(value)
103
- case Branch(children) =>
104
- each(0, children.size) { i => go(children.get(i)) }
105
- case Node(value, children) =>
238
+ case Branch1(_, child1) => go(child1.get)
239
+ case Node1(value, _, child1) =>
240
+ do emit(value); go(child1.get)
241
+ case Branch2(_, child1, _, child2) =>
242
+ go(child1.get); go(child2.get)
243
+ case Node2(value, _, child1, _, child2) =>
244
+ do emit(value); go(child1.get); go(child2.get)
245
+ case Branch3(_, child1, _, child2, _, child3) =>
246
+ go(child1.get); go(child2.get); go(child3.get)
247
+ case Node3(value, _, child1, _, child2, _, child3) =>
248
+ do emit(value); go(child1.get); go(child2.get); go(child3.get)
249
+ case Branch4(_, child1, _, child2, _, child3, _, child4) =>
250
+ go(child1.get); go(child2.get); go(child3.get); go(child4.get)
251
+ case Node4(value, _, child1, _, child2, _, child3, _, child4) =>
252
+ do emit(value); go(child1.get); go(child2.get); go(child3.get); go(child4.get)
253
+ case Branch256(children) =>
254
+ each(0, 256) { i => go(children.get(i)) }
255
+ case Node256(value, children) =>
106
256
  do emit(value)
107
- each(0, children.size) { i => go(children.get(i)) }
257
+ each(0, 256) { i => go(children.get(i)) }
108
258
  }
109
259
  go(dictionary.get)
110
260
  }
111
261
 
112
- /// Emit all key-value pairs stored in the dictionary as a push stream of Tagged elements.
262
+ /// Emit all key-value pairs stored in the dictionary as a push stream of
263
+ /// Tagged elements, in lexicographic key order (a key before its extensions).
113
264
  def eachTagged[A](dictionary: Dictionary[A]): Unit / emit[Tagged[A]] = {
114
- def fromBytesReverse(bytes: List[Byte], n: Int): ByteArray = {
115
- val key = bytearray::allocate(n)
116
- foreachIndex(bytes) { (i, b) => key.set(n - 1 - i, b) }
117
- key
265
+ var buffer = bytearray::allocate(32)
266
+ // `depth` grows by exactly one per level, so `depth <= buffer.size` always
267
+ // holds and a single doubling suffices.
268
+ def push(depth: Int, b: Byte): Unit = {
269
+ if (depth >= buffer.size) {
270
+ buffer = buffer.resize(buffer.size * 2)
271
+ }
272
+ buffer.set(depth, b)
118
273
  }
119
- def go(trie: Trie[A], key: List[Byte], n: Int): Unit =
274
+ def emitAt(depth: Int, value: A): Unit = {
275
+ val key = bytearray::allocate(depth)
276
+ // safe: `key` has exactly `depth` bytes, and `depth` is within `buffer`'s capacity.
277
+ bytearray::unsafeCopy(buffer, 0, key, 0, depth)
278
+ do emit(Tagged(key, value))
279
+ }
280
+ def go(trie: Trie[A], depth: Int): Unit =
120
281
  trie match {
121
282
  case Empty() => ()
122
- case Leaf(value) => do emit(Tagged(fromBytesReverse(key, n), value))
123
- case Branch(children) =>
124
- each(0, children.size) { i =>
125
- go(children.get(i), Cons(i.toByte, key), n + 1)
126
- }
127
- case Node(value, children) =>
128
- do emit(Tagged(fromBytesReverse(key, n), value))
129
- each(0, children.size) { i =>
130
- go(children.get(i), Cons(i.toByte, key), n + 1)
131
- }
283
+ case Leaf(value) => emitAt(depth, value)
284
+ case Branch1(byte1, child1) =>
285
+ push(depth, byte1); go(child1.get, depth + 1)
286
+ case Node1(value, byte1, child1) =>
287
+ emitAt(depth, value)
288
+ push(depth, byte1); go(child1.get, depth + 1)
289
+ case Branch2(byte1, child1, byte2, child2) =>
290
+ push(depth, byte1); go(child1.get, depth + 1)
291
+ push(depth, byte2); go(child2.get, depth + 1)
292
+ case Node2(value, byte1, child1, byte2, child2) =>
293
+ emitAt(depth, value)
294
+ push(depth, byte1); go(child1.get, depth + 1)
295
+ push(depth, byte2); go(child2.get, depth + 1)
296
+ case Branch3(byte1, child1, byte2, child2, byte3, child3) =>
297
+ push(depth, byte1); go(child1.get, depth + 1)
298
+ push(depth, byte2); go(child2.get, depth + 1)
299
+ push(depth, byte3); go(child3.get, depth + 1)
300
+ case Node3(value, byte1, child1, byte2, child2, byte3, child3) =>
301
+ emitAt(depth, value)
302
+ push(depth, byte1); go(child1.get, depth + 1)
303
+ push(depth, byte2); go(child2.get, depth + 1)
304
+ push(depth, byte3); go(child3.get, depth + 1)
305
+ case Branch4(byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
306
+ push(depth, byte1); go(child1.get, depth + 1)
307
+ push(depth, byte2); go(child2.get, depth + 1)
308
+ push(depth, byte3); go(child3.get, depth + 1)
309
+ push(depth, byte4); go(child4.get, depth + 1)
310
+ case Node4(value, byte1, child1, byte2, child2, byte3, child3, byte4, child4) =>
311
+ emitAt(depth, value)
312
+ push(depth, byte1); go(child1.get, depth + 1)
313
+ push(depth, byte2); go(child2.get, depth + 1)
314
+ push(depth, byte3); go(child3.get, depth + 1)
315
+ push(depth, byte4); go(child4.get, depth + 1)
316
+ case Branch256(children) =>
317
+ each(0, 256) { i => push(depth, i.toByte); go(children.get(i), depth + 1) }
318
+ case Node256(value, children) =>
319
+ emitAt(depth, value)
320
+ each(0, 256) { i => push(depth, i.toByte); go(children.get(i), depth + 1) }
132
321
  }
133
- go(dictionary.get, Nil(), 0)
134
- }
322
+ go(dictionary.get, 0)
323
+ }
324
+
@@ -72,7 +72,7 @@ extern def readln() at async: String =
72
72
  jsNode "$effekt.capture(callback => $effekt.readln(callback))"
73
73
  llvm """
74
74
  %result = call %Pos @c_io_readln()
75
- tail call void @resume_Pos(%Stack %stack, %Pos %result)
75
+ notail call void @resume_Pos(%Stack %stack, %Pos %result)
76
76
  ret void
77
77
  """
78
78
 
@@ -245,49 +245,49 @@ namespace internal {
245
245
  extern def openForReading(path: String) at async: Int =
246
246
  jsNode "$effekt.capture(callback => open(${path}, 'r', callback))"
247
247
  llvm """
248
- call void @c_fs_open_reading(%Pos ${path}, %Stack %stack)
248
+ notail call void @c_fs_open_reading(%Pos ${path}, %Stack %stack)
249
249
  ret void
250
250
  """
251
251
 
252
252
  extern def openForWriting(path: String) at async: Int =
253
253
  jsNode "$effekt.capture(callback => open(${path}, 'w', callback))"
254
254
  llvm """
255
- call void @c_fs_open_writing(%Pos ${path}, %Stack %stack)
255
+ notail call void @c_fs_open_writing(%Pos ${path}, %Stack %stack)
256
256
  ret void
257
257
  """
258
258
 
259
259
  extern def openForAppending(path: String) at async: Int =
260
260
  jsNode "$effekt.capture(callback => open(${path}, 'a', callback))"
261
261
  llvm """
262
- call void @c_fs_open_appending(%Pos ${path}, %Stack %stack)
262
+ notail call void @c_fs_open_appending(%Pos ${path}, %Stack %stack)
263
263
  ret void
264
264
  """
265
265
 
266
266
  extern def read(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int) at async: Int =
267
267
  jsNode "$effekt.capture(callback => read(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
268
268
  llvm """
269
- call void @c_fs_read(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
269
+ notail call void @c_fs_read(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
270
270
  ret void
271
271
  """
272
272
 
273
273
  extern def write(file: Int, buffer: ByteArray, offset: Int, size: Int, position: Int) at async: Int =
274
274
  jsNode "$effekt.capture(callback => write(${file}, ${buffer}, ${offset}, ${size}, ${position}, callback))"
275
275
  llvm """
276
- call void @c_fs_write(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
276
+ notail call void @c_fs_write(%Int ${file}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Int ${position}, %Stack %stack)
277
277
  ret void
278
278
  """
279
279
 
280
280
  extern def close(file: Int) at async: Int =
281
281
  jsNode "$effekt.capture(callback => close(${file}, callback))"
282
282
  llvm """
283
- call void @c_fs_close(%Int ${file}, %Stack %stack)
283
+ notail call void @c_fs_close(%Int ${file}, %Stack %stack)
284
284
  ret void
285
285
  """
286
286
 
287
287
  extern def mkdir(path: String) at async: Int =
288
288
  jsNode "$effekt.capture(callback => mkdir(${path}, callback))"
289
289
  llvm """
290
- call void @c_fs_mkdir(%Pos ${path}, %Stack %stack)
290
+ notail call void @c_fs_mkdir(%Pos ${path}, %Stack %stack)
291
291
  ret void
292
292
  """
293
293
 
@@ -147,28 +147,28 @@ namespace internal {
147
147
  extern def connect(host: String, port: Int) at async: Int =
148
148
  jsNode "$effekt.capture(k => tcp$connect(${host}, ${port}, k))"
149
149
  llvm """
150
- call void @c_tcp_connect(%Pos ${host}, %Int ${port}, %Stack %stack)
150
+ notail call void @c_tcp_connect(%Pos ${host}, %Int ${port}, %Stack %stack)
151
151
  ret void
152
152
  """
153
153
 
154
154
  extern def read(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
155
155
  jsNode "$effekt.capture(k => tcp$read(${handle}, ${buffer}, ${offset}, ${size}, k))"
156
156
  llvm """
157
- call void @c_tcp_read(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
157
+ notail call void @c_tcp_read(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
158
158
  ret void
159
159
  """
160
160
 
161
161
  extern def write(handle: Int, buffer: ByteArray, offset: Int, size: Int) at async: Int =
162
162
  jsNode "$effekt.capture(k => tcp$write(${handle}, ${buffer}, ${offset}, ${size}, k))"
163
163
  llvm """
164
- call void @c_tcp_write(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
164
+ notail call void @c_tcp_write(%Int ${handle}, %Pos ${buffer}, %Int ${offset}, %Int ${size}, %Stack %stack)
165
165
  ret void
166
166
  """
167
167
 
168
168
  extern def close(handle: Int) at async: Unit =
169
169
  jsNode "$effekt.capture(k => tcp$close(${handle}, k))"
170
170
  llvm """
171
- call void @c_tcp_close(%Int ${handle}, %Stack %stack)
171
+ notail call void @c_tcp_close(%Int ${handle}, %Stack %stack)
172
172
  ret void
173
173
  """
174
174
 
@@ -182,14 +182,14 @@ namespace internal {
182
182
  extern def listen(listener: Int, handler: Connection => Unit at {io, async, global}) at async: Int =
183
183
  jsNode "$effekt.capture(k => tcp$listen(${listener}, ${handler}, k))"
184
184
  llvm """
185
- call void @c_tcp_listen(%Int ${listener}, %Pos ${handler}, %Stack %stack)
185
+ notail call void @c_tcp_listen(%Int ${listener}, %Pos ${handler}, %Stack %stack)
186
186
  ret void
187
187
  """
188
188
 
189
189
  extern def shutdown(handle: Int) at async: Unit =
190
190
  jsNode "$effekt.capture(k => tcp$shutdown(${handle}, k))"
191
191
  llvm """
192
- call void @c_tcp_shutdown(%Int ${handle}, %Stack %stack)
192
+ notail call void @c_tcp_shutdown(%Int ${handle}, %Stack %stack)
193
193
  ret void
194
194
  """
195
195
 
@@ -74,18 +74,18 @@ extern def fire[A, B](signal: Signal[A, B], value: A) at async: B =
74
74
  store i64 2, ptr %statePtr
75
75
  store %Stack %stack, ptr %stackPtr
76
76
  store %Pos ${value}, ptr %valuePtr
77
- call void @erasePositive(%Pos ${signal})
77
+ notail call void @erasePositive(%Pos ${signal})
78
78
  ret void
79
79
  fire:
80
80
  %otherStack = load %Stack, ptr %stackPtr
81
81
  %otherValue = load %Pos, ptr %valuePtr
82
82
  store i64 3, ptr %statePtr
83
- call void @erasePositive(%Pos ${signal})
84
- call void @resume_Pos(%Stack %otherStack, %Pos ${value})
85
- call void @resume_Pos(%Stack %stack, %Pos %otherValue)
83
+ notail call void @erasePositive(%Pos ${signal})
84
+ notail call void @resume_Pos(%Stack %otherStack, %Pos ${value})
85
+ notail call void @resume_Pos(%Stack %stack, %Pos %otherValue)
86
86
  ret void
87
87
  panic:
88
- call void @exit(i64 1)
88
+ notail call void @exit(i32 1)
89
89
  ret void
90
90
  """
91
91
 
@@ -102,18 +102,18 @@ extern def wait[A, B](signal: Signal[A, B], value: B) at async: A =
102
102
  store i64 1, ptr %statePtr
103
103
  store %Stack %stack, ptr %stackPtr
104
104
  store %Pos ${value}, ptr %valuePtr
105
- call void @erasePositive(%Pos ${signal})
105
+ notail call void @erasePositive(%Pos ${signal})
106
106
  ret void
107
107
  wait:
108
108
  %otherStack = load %Stack, ptr %stackPtr
109
109
  %otherValue = load %Pos, ptr %valuePtr
110
110
  store i64 3, ptr %statePtr
111
- call void @erasePositive(%Pos ${signal})
112
- call void @resume_Pos(%Stack %otherStack, %Pos ${value})
113
- call void @resume_Pos(%Stack %stack, %Pos %otherValue)
111
+ notail call void @erasePositive(%Pos ${signal})
112
+ notail call void @resume_Pos(%Stack %otherStack, %Pos ${value})
113
+ notail call void @resume_Pos(%Stack %stack, %Pos %otherValue)
114
114
  ret void
115
115
  panic:
116
- call void @exit(i64 1)
116
+ notail call void @exit(i32 1)
117
117
  ret void
118
118
  """
119
119
 
@@ -7,6 +7,6 @@ extern llvm """
7
7
  extern def sleep(millis: Int) at async: Unit =
8
8
  js "$effekt.capture(k => setTimeout(() => k($effekt.unit), ${millis}))"
9
9
  llvm """
10
- call void @c_timer_start(%Int ${millis}, %Stack %stack)
10
+ notail call void @c_timer_start(%Int ${millis}, %Stack %stack)
11
11
  ret void
12
12
  """
@@ -11,22 +11,22 @@ extern llvm """
11
11
  extern def spawn[T](task: () => T at {io, async, global}) at async: Unit =
12
12
  js "$effekt.capture(k => { setTimeout(() => k($effekt.unit), 0); return $effekt.run(${task}) })"
13
13
  llvm """
14
- call void @c_yield(%Stack %stack)
15
- call void @run(%Pos ${task})
14
+ notail call void @c_yield(%Stack %stack)
15
+ notail call void @run(%Pos ${task})
16
16
  ret void
17
17
  """
18
18
 
19
19
  extern def yield() at async: Unit =
20
20
  js "$effekt.capture(k => setTimeout(() => k($effekt.unit), 0))"
21
21
  llvm """
22
- call void @c_yield(%Stack %stack)
22
+ notail call void @c_yield(%Stack %stack)
23
23
  ret void
24
24
  """
25
25
 
26
26
  extern def abort() at async: Nothing =
27
27
  js "$effekt.capture(k => undefined)"
28
28
  llvm """
29
- call void @eraseStack(%Stack %stack)
29
+ notail call void @eraseStack(%Stack %stack)
30
30
  ret void
31
31
  """
32
32