~sircmpwn/hare unlisted

137684c900b614378e8f032518a3b9b9ff9810d4 — Drew DeVault a month ago ab21c1b
docs: add storage.txt
1 files changed, 205 insertions(+), 0 deletions(-)

A docs/storage.txt
A docs/storage.txt => docs/storage.txt +205 -0
@@ 0,0 1,205 @@
			      Allocation & storage

A variable stored in a temporary:
	/* In function scope: */
	let x: int = 1234;
		-> %x =w copy 1234

	In this situation, x is of type int, and %x is of type =w, and the value
	of %x stores the value of x.

A variable stored on the stack:
	/* In function scope: */
	let x: int = 1234;
		-> %x =l alloc4 4
		-> storew 1234, %x
	&x; /* Force stack allocation */

	In this situation, x is of type int, and %x is of type =l, and the valu
	of %x is a pointer to a =w where the value of x is stored.

A variable stored on the heap:
	/* In function scope: */
	let x: &int = alloc(&int, 1234);
		-> %x =l call calloc(l 4, l 1)
		-> storew 1234, %x

	x is of type int, and %x is of type =l, and the value of %x is a pointer
	to a =w where the value of x is stored.

A statically allocated variable:
	/* In function scope: */
	static let x: int = 1234;
		-> /* In global scope (lowered to symbol): */
		-> data $.x.static.1 = { w 1234 }

	x is of type int, and $.x.static.1 is a generated name of type symbol,
	and is a pointer to a =w where the value of x is stored.

A global stored in the address space:
	/* In global scope: */
	let x: int = 1234;
		-> data $x = { w 1234 }

	In this situation, x is of type int, and $x is of type symbol, and is a
	pointer to a =w where the value of x is stored.

Aggregate types are always stored indirectly:
	/* In function scope: */
	let x: [4]int = [4, 3, 2, 1];
		-> %x =l alloc4 16 /* 4 × 4 = 16 */
		-> %.offset.1 =l mul 0, 4
		-> %.index.1 =l add %x, %.offset.1
		-> storew 4, %index.1
		-> /* ... */

	The type of x is [4]int, but because it's an aggregate type, the gen
	step will infer that %x is a pointer and it's always accessed
	indirectly.

	let x = struct { x: int = 4, y: int = 2 };
		-> %x =l alloc4 8
		-> %.field.1 =l add %x, 0
		-> storew 4, %.field.1
		-> %.field.2 =l add %x, 0
		-> storew 4, %.field.2

	/* In global scope: */
	let x: [4]int = [1, 2, 3, 4];
		-> data $x = { w 4, w 3, w 2, w 1 }

Nested aggregate types are embedded in their parents:
	/* In function scope: */
	let x: [2][2]int = [[4, 3], [2, 1]];
		-> %x =l alloc4 16 /* 4 × 4 = 16 */
		-> %.offset.1 =l mul 0, 4
		-> %.index.1 =l add %x, %.offset.1
		-> storew 4, %index.1
		-> /* ... */

	let x = struct { a: struct { x: int = 4 }, b: struct { y: int = 2 } };
		-> %x =l alloc4 8
		-> %.field.1 =l add %x, 0
		-> storew 4, %.field.1
		-> %.field.2 =l add %x, 0
		-> storew 4, %.field.2

	/* In global scope: */
	let x: [4]int = [1, 2, 3, 4];
		-> data $x = { w 4, w 3, w 2, w 1 }

	Note that this is exactly the same as the previous example.

A string literal is lowered to a symbol:
	/* In function scope: */
	let x: str = "Hello, world!";
		-> data $x = { l 13, b "Hello, world!", b 0 }
		-> %x =l copy $str

	x is of type str, and is immutable. The string contents are UTF-8.

	let x: *str = "Hello, world!";
		-> data $x = { l 13, b "Hello, world!", b 0 }
		-> %x =l copy $str

	x is of type *str, the pointer is mutable, and the string is not. Note
	that the generated code is identical.

	Note that aggregate types may be implicitly cast to a borrowed pointer
	of that type. The same is true of the following:

	let x: *[4]int = [1, 2, 3, 4];

	Back to strings, an owned pointer has to be made via the stdlib:

	let x: &str = strings::dup("Hello, world!");
		-> data $.string.0 = { l 13, b "Hello, world!", b 0 }
		-> %x =l call strings.dup(l $.string.0)

	x is of type &str. The pointer is mutable, but owned, and must be
	transferred before mutating. The string itself is immutable.

	/* Misc. string usage examples */
	let x: &str = strings::dup("Hello, ");
	x = strings::append(^x, username);
	x = strings::append(^x, "!\n");

	/* or: */
	let x: &str = strings::dup("Hello");
	x = fmt::sprintf("%s, %s!\n", ^x, username);

	/* or (obvious answer): */
	let x: &str = fmt::sprintf("Hello, %s!", username);

				     Access

There are several kinds of accesses:

- Identifier access (accessing locals & globals by name)
- Array member access
- Slice subset access
- Struct field access

			       Identifier access

In order to access an identifier, we need to decide on an access strategy:
direct access (via the temporary) or indirect access (dereferencing a pointer).

When accessing a local:
	Is it static?		-> Indirect access via symbol
	Is it stack-allocated?	-> Indirect access via temporary
	Is it an aggregate?	-> Direct access to the temporary *
	Otherwise?		-> Direct access to the temporary

When accessing a global:
	Is it an aggregate?	-> Direct access to the temporary *
	Otherwise?		-> Indirect access via symbol

* Note: aggregate types are only accessed directly (rather than accessing their
  constituent values) as the parameters to functions or builtins.

When accessing a parameter:
	Is it an aggregate?	-> TODO: qbe aggregate types
	Otherwise?		-> Direct access to the temporary

			      Array member access
			      Slice member access
			      Struct field access

1. Because <aggregate> and *<aggregate> are both stored indirectly, one level of
   pointer indirection may be removed from the type without additional
   computation.
2. Reduce all aggregate member accesses to determine the address of the member.
3. Generate an indirect access via this computed address.

			      Slice subset access

The caller allocates storage for the resulting slice. Compute the appropriate
values for the start address and length, and store these values in this slice.
The result type of this operation is a const slice of the source array or source
slice member type.

	let x = [1, 2, 3, 4];
	let y: const []int = x[..2];
	assert(y[0] == 1 && y[1] == 2);

			    Assignment (= operator)

TODO

			  Address taking (& operator)

TODO

			   Dereferencing (* operator)

TODO

			     Implicit dereferencing

Array member and struct field accesses are automatically dereferenced, e.g.

	let coords: *struct { x: int, y: int } = /* ... */;
	coords.x; /* coords is dereferenced */

TODO: More details