aboutsummaryrefslogtreecommitdiffhomepage
path: root/notes/generics_64_128_bit.txt
blob: f237489bdabff9df3a08a7b175bce800fdc14908 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

Generics and 64 bit / 128 bit types
===================================

SLUL uses type erasure, so each type parameter ("slot" type) is actually
some form of "generic_word" type under the hood. This is a type that can
fit any of the following types:

- bool
- integers with <= 32 bits
- usize and ssize types
- pointers to data

This means that 64-bit integer- and function pointer types cannot be used
in type parameters, because the size could be larger that the maximum
size on *some* platform (and SLUL code is meant to be 100% portable, or
to give compiler error if not).

(Note that funcref's don't necessarilly support comparison,
so they must not be allowed in slot types anyway.)

Slots that are larger than the register size
--------------------------------------------

This would lead to much less efficient code, in particular if the ABI
does not allow placing large values in register pairs.

Mixed-size access to slot fields
--------------------------------

Two problems need to be taken care of in order to handle mixed-size
slot fields:

1. Padding
2. Endianness

The padding issue is easy to solve. Just zero-pad on write.
Reads can use either the "real" size or the generic/maximum size
(it should give the same result on little-endian platforms).

However, on big-endian platforms, it is important to ensure that
loads/stores always result in exactly the same value. Loading
a byte vs a 64-bit value vs a 128-bit value at the same address
will give different results even if the zero-padding is there.

One way to acheive that is to always load/store using the widest possible
method. Another way is to load the smallest unit, but take the padding
into account when calculating the load address of the slot field.

Possible solution with low-order bits
-------------------------------------

On all platforms that I can think of, at least one of the following
holds true:

1. The size of a "generic_word" is equal to the largest possible scalar
   type. I.e. it's at least 64 bits and function pointers are no larger
   than that size.
2. The alignment requirement of the "generic_word" type is at least 2
   bytes.

In case 1, all scalar types can fit in generic "slot" type.

In case 2, the low-order bit is used as a "mode bit" to switch between
normal and "large mode". When a reference has the "large mode" bit set,
all of the "slot" types are taken to have a size that can accomodate any
type, even e.g. 64 bit types on 32 bit platforms.

This would means that any statements that access "slot" types would have
to be duplicated. In case the "slot" types are allocated as local
variables, the emitted code would have to take both cases into account
(either by always using a large type, or conditionally selecting the
stack size and/or register usage). Or there could be two separate functions
(this is how most other languages do it).

Extension to 128 bit
--------------------

This can be extended to 128 bits also. So maybe it should be enabled for
64 bit platforms as well, just in case an int128 type will be added later?