Skip to content

Commit c9c2ec6

Browse files
giacomocavalierilpil
authored andcommitted
Avoid repeating slices when aliasing constant values
Instead of taking a slice twice, if a segment pattern is written like this: `1 as a`, we bind the `a` variable to the constant `1` directly and avoid taking a second slice after performing the runtime check.
1 parent 51181ce commit c9c2ec6

8 files changed

+161
-14
lines changed

compiler-core/src/exhaustiveness.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -286,18 +286,28 @@ impl Branch {
286286
}
287287

288288
Some(test) => match test {
289+
// If we have `_ as a` we treat that as a regular variable
290+
// assignment.
291+
BitArrayTest::Match(MatchTest {
292+
value: BitArrayMatchedValue::Assign { name, value },
293+
read_action,
294+
}) if value.is_discard() => {
295+
*test = BitArrayTest::Match(MatchTest {
296+
value: BitArrayMatchedValue::Variable(name.clone()),
297+
read_action: read_action.clone(),
298+
});
299+
continue;
300+
}
301+
289302
// Just like regular assigns, those patterns are unrefutable
290303
// and will become assignments in the branch's body.
291304
BitArrayTest::Match(MatchTest {
292305
value: BitArrayMatchedValue::Assign { name, value },
293306
read_action,
294307
}) => {
295-
let bit_array = check.var.clone();
296-
self.body.assign_bit_array_slice(
297-
name.clone(),
298-
bit_array,
299-
read_action.clone(),
300-
);
308+
self.body
309+
.assign_segment_constant_value(name.clone(), value.as_ref());
310+
301311
// We will still need to check the aliased value!
302312
*test = BitArrayTest::Match(MatchTest {
303313
value: value.as_ref().clone(),
@@ -362,6 +372,14 @@ pub enum BoundValue {
362372
///
363373
LiteralString(EcoString),
364374

375+
/// `let a = 123`
376+
///
377+
LiteralInt(BigInt),
378+
379+
/// `let a = 12.2`
380+
///
381+
LiteralFloat(EcoString),
382+
365383
/// `let a = sliceAsInt(bit_array, 0, 16, ...)`
366384
///
367385
BitArraySlice {
@@ -403,6 +421,21 @@ impl Body {
403421
},
404422
))
405423
}
424+
425+
fn assign_segment_constant_value(&mut self, name: EcoString, value: &BitArrayMatchedValue) {
426+
let value = match value {
427+
BitArrayMatchedValue::LiteralFloat(value) => BoundValue::LiteralFloat(value.clone()),
428+
BitArrayMatchedValue::LiteralInt(value) => BoundValue::LiteralInt(value.clone()),
429+
BitArrayMatchedValue::LiteralString(value) => BoundValue::LiteralString(value.clone()),
430+
BitArrayMatchedValue::Variable(_)
431+
| BitArrayMatchedValue::Discard(_)
432+
| BitArrayMatchedValue::Assign { .. } => {
433+
panic!("aliased non constant value: {:#?}", value)
434+
}
435+
};
436+
437+
self.bindings.push((name, value))
438+
}
406439
}
407440

408441
/// A user defined pattern such as `Some((x, 10))`.
@@ -981,6 +1014,19 @@ pub enum BitArrayMatchedValue {
9811014
},
9821015
}
9831016

1017+
impl BitArrayMatchedValue {
1018+
pub fn is_discard(&self) -> bool {
1019+
match self {
1020+
BitArrayMatchedValue::Discard(_) => true,
1021+
BitArrayMatchedValue::LiteralFloat(_)
1022+
| BitArrayMatchedValue::LiteralInt(_)
1023+
| BitArrayMatchedValue::LiteralString(_)
1024+
| BitArrayMatchedValue::Variable(_)
1025+
| BitArrayMatchedValue::Assign { .. } => false,
1026+
}
1027+
}
1028+
}
1029+
9841030
impl BitArrayTest {
9851031
// TODO: for these tests we could also implement a more sophisticated
9861032
// approach for `Match` tests. This is described in the linked paper as

compiler-core/src/javascript/decision.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,8 @@ impl<'generator, 'module, 'a> Variables<'generator, 'module, 'a> {
820820
let assigned_value = match value {
821821
BoundValue::Variable(variable) => self.get_value(variable).to_doc(),
822822
BoundValue::LiteralString(value) => string(value),
823+
BoundValue::LiteralFloat(value) => float(value),
824+
BoundValue::LiteralInt(value) => eco_string_int(eco_format!("{value}")),
823825
BoundValue::BitArraySlice {
824826
bit_array,
825827
read_action,

compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__bit_array_assignment_float.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ export function main() {
2525
{ value: $ }
2626
)
2727
}
28-
let pi = bitArraySliceToFloat($, 0, 64, true);
28+
let pi = 3.14;
2929
return pi;
3030
}

compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__bit_array_assignment_int.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ export function main() {
2525
{ value: $ }
2626
)
2727
}
28-
let a = $.byteAt(0);
28+
let a = 1;
2929
return a;
3030
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
source: compiler-core/src/javascript/tests/bit_arrays.rs
3+
expression: "\npub fn main() {\n let assert <<\"Hello, world!\" as message:utf8>> = <<\"Hello, world!\">>\n message\n}\n"
4+
---
5+
----- SOURCE CODE
6+
7+
pub fn main() {
8+
let assert <<"Hello, world!" as message:utf8>> = <<"Hello, world!">>
9+
message
10+
}
11+
12+
13+
----- COMPILED JAVASCRIPT
14+
import { makeError, toBitArray, stringBits } from "../gleam.mjs";
15+
16+
export function main() {
17+
let $ = toBitArray([stringBits("Hello, world!")]);
18+
if (
19+
$.bitSize !== 64 ||
20+
$.byteAt(0) !== 72 ||
21+
$.byteAt(1) !== 101 ||
22+
$.byteAt(2) !== 108 ||
23+
$.byteAt(3) !== 108 ||
24+
$.byteAt(4) !== 111 ||
25+
$.byteAt(5) !== 44 ||
26+
$.byteAt(6) !== 32 ||
27+
$.byteAt(7) !== 119 ||
28+
$.byteAt(8) !== 111 ||
29+
$.byteAt(9) !== 114 ||
30+
$.byteAt(10) !== 108 ||
31+
$.byteAt(11) !== 100 ||
32+
$.byteAt(12) !== 33
33+
) {
34+
throw makeError(
35+
"let_assert",
36+
"my/mod",
37+
3,
38+
"main",
39+
"Pattern match failed, no pattern matched the value.",
40+
{ value: $ }
41+
)
42+
}
43+
let message = "Hello, world!";
44+
return message;
45+
}

compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__case_bit_array_assignment_float.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ import { bitArraySliceToFloat } from "../gleam.mjs";
1919
export function go(x) {
2020
if (x.bitSize === 64) {
2121
if (bitArraySliceToFloat(x, 0, 64, true) === 3.14) {
22-
let pi = bitArraySliceToFloat(x, 0, 64, true);
22+
let pi = 3.14;
2323
return pi;
2424
} else {
2525
if (bitArraySliceToFloat(x, 0, 64, true) === 1.1) {
2626
if ((x.bitSize - 64) % 8 === 0) {
27-
let pi = bitArraySliceToFloat(x, 0, 64, true);
27+
let pi = 1.1;
2828
return pi;
2929
} else {
3030
return 1.1;
@@ -37,7 +37,7 @@ export function go(x) {
3737
if (x.bitSize >= 64) {
3838
if (bitArraySliceToFloat(x, 0, 64, true) === 1.1) {
3939
if ((x.bitSize - 64) % 8 === 0) {
40-
let pi = bitArraySliceToFloat(x, 0, 64, true);
40+
let pi = 1.1;
4141
return pi;
4242
} else {
4343
return 1.1;

compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__bit_arrays__case_bit_array_assignment_int.snap

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ pub fn go(x) {
1717
export function go(x) {
1818
if (x.bitSize === 8) {
1919
if (x.byteAt(0) === 1) {
20-
let n = x.byteAt(0);
20+
let n = 1;
2121
return n;
2222
} else {
2323
if (x.byteAt(0) === 2) {
2424
if ((x.bitSize - 8) % 8 === 0) {
25-
let n = x.byteAt(0);
25+
let n = 2;
2626
return n;
2727
} else {
2828
return 1;
@@ -35,7 +35,7 @@ export function go(x) {
3535
if (x.bitSize >= 8) {
3636
if (x.byteAt(0) === 2) {
3737
if ((x.bitSize - 8) % 8 === 0) {
38-
let n = x.byteAt(0);
38+
let n = 2;
3939
return n;
4040
} else {
4141
return 1;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
source: compiler-core/src/javascript/tests/bit_arrays.rs
3+
expression: "\npub fn go(x) {\n case x {\n <<\"Hello\" as message>>\n | <<\"Jak\" as message, _:bytes>> -> message\n _ -> \"wibble\"\n }\n}\n"
4+
---
5+
----- SOURCE CODE
6+
7+
pub fn go(x) {
8+
case x {
9+
<<"Hello" as message>>
10+
| <<"Jak" as message, _:bytes>> -> message
11+
_ -> "wibble"
12+
}
13+
}
14+
15+
16+
----- COMPILED JAVASCRIPT
17+
export function go(x) {
18+
if (x.bitSize === 64) {
19+
if (x.byteAt(0) === 72 &&
20+
x.byteAt(1) === 101 &&
21+
x.byteAt(2) === 108 &&
22+
x.byteAt(3) === 108 &&
23+
x.byteAt(4) === 111) {
24+
let message = "Hello";
25+
return message;
26+
} else {
27+
if (x.byteAt(0) === 74 && x.byteAt(1) === 97 && x.byteAt(2) === 107) {
28+
if ((x.bitSize - 64) % 8 === 0) {
29+
let message = "Jak";
30+
return message;
31+
} else {
32+
return "wibble";
33+
}
34+
} else {
35+
return "wibble";
36+
}
37+
}
38+
} else {
39+
if (x.bitSize >= 64) {
40+
if (x.byteAt(0) === 74 && x.byteAt(1) === 97 && x.byteAt(2) === 107) {
41+
if ((x.bitSize - 64) % 8 === 0) {
42+
let message = "Jak";
43+
return message;
44+
} else {
45+
return "wibble";
46+
}
47+
} else {
48+
return "wibble";
49+
}
50+
} else {
51+
return "wibble";
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)