Skip to content

Commit f32a6da

Browse files
committed
feat: Cleanup to shrink smallest program size
This is a rather small pr, I was looking through the binary of our smallest program and noticed a few things that were being left behind. Not very important but we can save a few bytes by removing the unnecessary function wrapping our exception setup, this function was required when exception handling was unsafe however it's now completely safe so there is no reason to map it. The bigger change is I noticed that `bigIntToString` was doing some weird things, firstly the `_SIZES` array wasn't actually ever being used however it was causing the function to require a closure which was being setup in every program and adding a rather substantial amount of size. Secondly because we had a helper function that used`_DIGITS` inside of the toString method this created a closure and because we were not lazily initializing digits this was adding quite a few bytes to the program, with a few simple changes this cuts program size and I tracked length along with the result buffer when stringifying which saves an iteration over the result buffer, and the corresponding helper.
1 parent 7354757 commit f32a6da

3 files changed

Lines changed: 44 additions & 94 deletions

File tree

compiler/test/suites/basic_functionality.re

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,6 @@ describe("basic functionality", ({test, testSkip}) => {
485485
~config_fn=smallestFileConfig,
486486
"smallest_grain_program",
487487
"",
488-
329,
488+
284,
489489
);
490490
});

stdlib/pervasives.gr

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -230,21 +230,17 @@ provide primitive unbox = "@unbox"
230230

231231
// Setup exception printing
232232
primitive elideTypeInfo = "@meta.elide_type_info"
233-
@unsafe
234-
let setupExceptions = () => {
235-
Exception.registerPrinter(e => {
236-
match (e) {
237-
Failure(msg) => Some("Failure: " ++ msg),
238-
InvalidArgument(msg) => Some("Invalid argument: " ++ msg),
239-
_ => None,
240-
}
241-
})
242-
243-
// If type information is elided, remove dependency on toString as
244-
// it will have no effect on exceptions
245-
if (!elideTypeInfo) {
246-
Exception.registerBasePrinter(e => toString(e))
233+
234+
Exception.registerPrinter(e => {
235+
match (e) {
236+
Failure(msg) => Some("Failure: " ++ msg),
237+
InvalidArgument(msg) => Some("Invalid argument: " ++ msg),
238+
_ => None,
247239
}
248-
}
240+
})
249241

250-
setupExceptions()
242+
// If type information is elided, remove dependency on toString as
243+
// it will have no effect on exceptions
244+
if (!elideTypeInfo) {
245+
Exception.registerBasePrinter(e => toString(e))
246+
}

stdlib/runtime/bigint.gr

Lines changed: 31 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -691,70 +691,35 @@ let countTrailingZeroBits = num => {
691691
result
692692
}
693693

694-
let _DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"
695-
// maximum number of digits that can fully fit a uint64 (for each valid base):
696-
let _SIZES = [>
697-
0,
698-
0,
699-
64, // 2
700-
40, // 3
701-
32, // 4
702-
27, // 5
703-
24, // 6
704-
22, // 7
705-
21, // 8
706-
20, // 9
707-
19, // 10
708-
18, // 11
709-
17, // 12
710-
17, // 13
711-
16, // 14
712-
16, // 15
713-
16, // 16
714-
15, // 17
715-
15, // 18
716-
15, // 19
717-
14, // 20
718-
14, //
719-
14, //
720-
14, // 23
721-
13, // 24
722-
13, //
723-
13, //
724-
13, //
725-
13, //
726-
13, //
727-
13, //
728-
12,
729-
12,
730-
12,
731-
12,
732-
12,
733-
12,
734-
]
694+
@unsafe
695+
let mut _DIGITS = WasmRef.fromGrain(void)
696+
697+
@unsafe
698+
let getDigit = n => {
699+
if (WasmRef.isRefI31(_DIGITS)) {
700+
_DIGITS = WasmRef.fromGrain("0123456789abcdefghijklmnopqrstuvwxyz")
701+
}
702+
DS.tagChar(WasmArrayRef.getI8U(DS.getStringArrayRef(_DIGITS), n))
703+
}
735704

736705
@unsafe
737706
provide let bigIntToString = (num, base) => {
738707
use WasmI32.{ (+), (<), (>) }
739-
let getDigit = n =>
740-
WasmArrayRef.getI8U(DS.getStringArrayRef(WasmRef.fromGrain(_DIGITS)), n)
741708
if (base < 2n || base > 32n) {
742709
throw Exception.InvalidArgument("toString base must be in range [2,32]")
743710
}
744711
use WasmI64.{ (-), (*), (|), (<<), (>) }
745712
if (eqz(num)) {
746713
"0"
747714
} else {
748-
let size = DS.untagSimpleNumber(_SIZES[DS.tagSimpleNumber(base)])
749715
use WasmI32.{ (==), (-) as subWasmI32 }
750716
let mut result = []
717+
let mut length = 0n
751718
if (base == 2n || base == 4n || base == 8n || base == 16n || base == 32n) {
752719
// if base is a power of two, use optimized path
753720
let bits = WasmI64.extendI32U(WasmI32.ctz(base))
754721
let mask = (1N << bits) - 1N
755722
let numLimbs = getSize(num)
756-
let totalBits = 64N * WasmI64.extendI32U(numLimbs)
757-
- WasmI64.clz(getLimb(num, subWasmI32(numLimbs, 1n)))
758723
let mut acc = 0N
759724
let mut accBits = 0N
760725
for (let mut i = 0n; i < numLimbs; i += 1n) {
@@ -763,10 +728,9 @@ provide let bigIntToString = (num, base) => {
763728
acc = acc | limb << accBits
764729
accBits += 64N
765730
while (accBits >= bits) {
766-
result = [
767-
DS.tagChar(getDigit(WasmI32.wrapI64(acc & mask))),
768-
...result
769-
]
731+
use WasmI32.{ (+) }
732+
result = [getDigit(WasmI32.wrapI64(acc & mask)), ...result]
733+
length += 1n
770734
acc = acc >>> bits
771735
if (accBits > 64N) {
772736
acc = limb >>> (64N - (accBits - bits))
@@ -775,13 +739,13 @@ provide let bigIntToString = (num, base) => {
775739
}
776740
}
777741
if (acc > 0N) {
778-
result = [DS.tagChar(getDigit(WasmI32.wrapI64(acc))), ...result]
742+
result = [getDigit(WasmI32.wrapI64(acc)), ...result]
743+
length += 1n
779744
}
780745
} else {
781746
let base = WasmI64.extendI32U(base)
782747
let d = base
783748
let mut tmp = clone(num)
784-
setFlag(tmp, _IS_NEGATIVE, 0n)
785749
while (!eqz(tmp)) {
786750
use WasmI32.{ (-), (<<), (>=) }
787751
let tmpCopy = tmp
@@ -801,45 +765,35 @@ provide let bigIntToString = (num, base) => {
801765
}
802766
}
803767
tmp = trimNumber(tmp)
804-
result = [
805-
DS.tagChar(getDigit(WasmI32.wrapI64(WasmI64.remU(c, base)))),
806-
...result
807-
]
768+
result = [getDigit(WasmI32.wrapI64(WasmI64.remU(c, base))), ...result]
769+
length += 1n
808770
}
809771
}
810-
while (match (result) {
811-
[c, ...tl] when DS.untagChar(c) == DS.untagChar('0') => true,
812-
_ => false,
813-
}) {
772+
while (true) {
814773
match (result) {
815-
[c, ...tl] => result = tl,
816-
_ => void, // <- impossible
774+
[c, ...tl] when DS.untagChar(c) == DS.untagChar('0') => {
775+
use WasmI32.{ (-) }
776+
result = tl
777+
length -= 1n
778+
},
779+
_ => break,
817780
}
818781
}
819782
if (flagIsSet(num, _IS_NEGATIVE)) {
820783
result = ['-', ...result]
784+
length += 1n
821785
}
822-
@unsafe
823-
let rec computeLength = (lst, acc) => {
824-
match (lst) {
825-
[] => acc,
826-
[_, ...tl] => computeLength(tl, acc + 1n),
827-
}
828-
}
829-
let length = computeLength(result, 0n)
830786
let ret = DS.allocateString(length)
831787
let retArray = DS.getStringArrayRef(ret)
832-
@unsafe
833-
let rec populateString = (lst, idx, str) => {
834-
match (lst) {
835-
[] => void,
788+
for (let mut i = 0n; i < length; i += 1n) {
789+
match (result) {
836790
[hd, ...tl] => {
837-
WasmArrayRef.setI8(str, idx, DS.untagChar(hd))
838-
populateString(tl, idx + 1n, str)
791+
WasmArrayRef.setI8(retArray, i, DS.untagChar(hd))
792+
result = tl
839793
},
794+
[] => break, // <- impossible as length == result.length
840795
}
841796
}
842-
populateString(result, 0n, retArray)
843797
WasmRef.toGrain(ret): String
844798
}
845799
}

0 commit comments

Comments
 (0)