Browse Source

Implement `f16` functionality

gingerBill 4 years ago
parent
commit
54e6c50769

+ 15 - 0
core/builtin/builtin.odin

@@ -43,6 +43,7 @@ complex32     :: complex32;
 complex64     :: complex64;
 complex128    :: complex128;
 
+quaternion64  :: quaternion64;
 quaternion128 :: quaternion128;
 quaternion256 :: quaternion256;
 
@@ -76,6 +77,17 @@ u64be         :: u64be;
 i128be        :: i128be;
 u128be        :: u128be;
 
+
+f16le         :: f16le;
+f32le         :: f32le;
+f64le         :: f64le;
+
+f16be         :: f16be;
+f32be         :: f32be;
+f64be         :: f64be;
+
+
+
 // Procedures
 len :: proc(array: Array_Type) -> int ---
 cap :: proc(array: Array_Type) -> int ---
@@ -103,3 +115,6 @@ min   :: proc(values: ..T) -> T ---
 max   :: proc(values: ..T) -> T ---
 abs   :: proc(value: T) -> T ---
 clamp :: proc(value, minimum, maximum: T) -> T ---
+
+soa_zip :: proc(slices: ...) -> #soa[]Struct ---
+soa_unzip :: proc(value: $S/#soa[]$E) -> (slices: ...) ---

+ 1 - 0
core/encoding/json/marshal.odin

@@ -89,6 +89,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
 	case Type_Info_Float:
 		val: f64;
 		switch f in a {
+		case f16: val = f64(f);
 		case f32: val = f64(f);
 		case f64: val = f64(f);
 		}

+ 4 - 0
core/fmt/fmt.odin

@@ -948,6 +948,7 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
 
 		u: u64;
 		switch bit_size {
+		case 16: u = u64(transmute(u16)f16(v));
 		case 32: u = u64(transmute(u32)f32(v));
 		case 64: u = transmute(u64)v;
 		case: panic("Unhandled float size");
@@ -2023,12 +2024,15 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
 	case any:        fmt_arg(fi,  a, verb);
 	case rune:       fmt_rune(fi, a, verb);
 
+	case f16:        fmt_float(fi, f64(a), 16, verb);
 	case f32:        fmt_float(fi, f64(a), 32, verb);
 	case f64:        fmt_float(fi, a,      64, verb);
 
+	case f16le:      fmt_float(fi, f64(a), 16, verb);
 	case f32le:      fmt_float(fi, f64(a), 32, verb);
 	case f64le:      fmt_float(fi, f64(a), 64, verb);
 
+	case f16be:      fmt_float(fi, f64(a), 16, verb);
 	case f32be:      fmt_float(fi, f64(a), 32, verb);
 	case f64be:      fmt_float(fi, f64(a), 64, verb);
 

+ 2 - 2
core/odin/tokenizer/tokenizer.odin

@@ -431,9 +431,9 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, str
 					}
 
 					switch digit_count {
-					case 8, 16: break;
+					case 4, 8, 16: break;
 					case:
-						error(t, t.offset, "invalid hexadecimal floating-point number, expected 8 or 16 digits, got %d", digit_count);
+						error(t, t.offset, "invalid hexadecimal floating-point number, expected 4, 8, or 16 digits, got %d", digit_count);
 					}
 				}
 

+ 167 - 0
core/runtime/internal.odin

@@ -1,5 +1,7 @@
 package runtime
 
+import "intrinsics"
+
 bswap_16 :: proc "none" (x: u16) -> u16 {
 	return x>>8 | x<<8;
 }
@@ -25,6 +27,12 @@ bswap_128 :: proc "none" (x: u128) -> u128 {
 	return transmute(u128)z;
 }
 
+bswap_f16 :: proc "none" (f: f16) -> f16 {
+	x := transmute(u16)f;
+	z := bswap_16(x);
+	return transmute(f16)z;
+
+}
 
 bswap_f32 :: proc "none" (f: f32) -> f32 {
 	x := transmute(u32)f;
@@ -410,6 +418,12 @@ foreign {
 	@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
 	@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
 }
+abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
+	foreign {
+		@(link_name="llvm.fabs.f16") _abs :: proc "none" (x: f16) -> f16 ---
+	}
+	return _abs(x);
+}
 abs_f32 :: #force_inline proc "contextless" (x: f32) -> f32 {
 	foreign {
 		@(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 ---
@@ -423,6 +437,12 @@ abs_f64 :: #force_inline proc "contextless" (x: f64) -> f64 {
 	return _abs(x);
 }
 
+min_f16 :: proc(a, b: f16) -> f16 {
+	foreign {
+		@(link_name="llvm.minnum.f16") _min :: proc "none" (a, b: f16) -> f16 ---
+	}
+	return _min(a, b);
+}
 min_f32 :: proc(a, b: f32) -> f32 {
 	foreign {
 		@(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 ---
@@ -435,6 +455,12 @@ min_f64 :: proc(a, b: f64) -> f64 {
 	}
 	return _min(a, b);
 }
+max_f16 :: proc(a, b: f16) -> f16 {
+	foreign {
+		@(link_name="llvm.maxnum.f16") _max :: proc "none" (a, b: f16) -> f16 ---
+	}
+	return _max(a, b);
+}
 max_f32 :: proc(a, b: f32) -> f32 {
 	foreign {
 		@(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 ---
@@ -448,6 +474,10 @@ max_f64 :: proc(a, b: f64) -> f64 {
 	return _max(a, b);
 }
 
+abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
+	r, i := real(x), imag(x);
+	return f16(_sqrt_f32(f32(r*r + i*i)));
+}
 abs_complex64 :: #force_inline proc "contextless" (x: complex64) -> f32 {
 	r, i := real(x), imag(x);
 	return _sqrt_f32(r*r + i*i);
@@ -456,6 +486,10 @@ abs_complex128 :: #force_inline proc "contextless" (x: complex128) -> f64 {
 	r, i := real(x), imag(x);
 	return _sqrt_f64(r*r + i*i);
 }
+abs_quaternion64 :: #force_inline proc "contextless" (x: quaternion64) -> f16 {
+	r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
+	return f16(_sqrt_f32(f32(r*r + i*i + j*j + k*k)));
+}
 abs_quaternion128 :: #force_inline proc "contextless" (x: quaternion128) -> f32 {
 	r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
 	return _sqrt_f32(r*r + i*i + j*j + k*k);
@@ -465,6 +499,26 @@ abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64
 	return _sqrt_f64(r*r + i*i + j*j + k*k);
 }
 
+
+quo_complex32 :: proc "contextless" (n, m: complex32) -> complex32 {
+	e, f: f16;
+
+	if abs(real(m)) >= abs(imag(m)) {
+		ratio := imag(m) / real(m);
+		denom := real(m) + ratio*imag(m);
+		e = (real(n) + imag(n)*ratio) / denom;
+		f = (imag(n) - real(n)*ratio) / denom;
+	} else {
+		ratio := real(m) / imag(m);
+		denom := imag(m) + ratio*real(m);
+		e = (real(n)*ratio + imag(n)) / denom;
+		f = (imag(n)*ratio - real(n)) / denom;
+	}
+
+	return complex(e, f);
+}
+
+
 quo_complex64 :: proc "contextless" (n, m: complex64) -> complex64 {
 	e, f: f32;
 
@@ -501,6 +555,18 @@ quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 {
 	return complex(e, f);
 }
 
+mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
+	q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
+	r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
+
+	t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3;
+	t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2;
+	t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1;
+	t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0;
+
+	return quaternion(t0, t1, t2, t3);
+}
+
 mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
 	q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
 	r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
@@ -525,6 +591,20 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
 	return quaternion(t0, t1, t2, t3);
 }
 
+quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
+	q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
+	r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
+
+	invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3);
+
+	t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2;
+	t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2;
+	t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2;
+	t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2;
+
+	return quaternion(t0, t1, t2, t3);
+}
+
 quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
 	q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
 	r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
@@ -552,3 +632,90 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
 
 	return quaternion(t0, t1, t2, t3);
 }
+
+@(link_name="__truncsfhf2")
+truncsfhf2 :: proc "c" (value: f32) -> u16 {
+	v: struct #raw_union { i: u32, f: f32 };
+	i, s, e, m: i32;
+
+	v.f = value;
+	i = i32(v.i);
+
+	s =  (i >> 16) & 0x00008000;
+	e = ((i >> 23) & 0x000000ff) - (127 - 15);
+	m =   i        & 0x007fffff;
+
+
+	if (e <= 0) {
+		if (e < -10) {
+			return u16(s);
+		}
+		m = (m | 0x00800000) >> u32(1 - e);
+
+		if (m & 0x00001000) != 0 {
+			m += 0x00002000;
+		}
+
+		return u16(s | (m >> 13));
+	} else if (e == 0xff - (127 - 15)) {
+		if (m == 0) {
+			return u16(s | 0x7c00); /* NOTE(bill): infinity */
+		} else {
+			/* NOTE(bill): NAN */
+			m >>= 13;
+			return u16(s | 0x7c00 | m | i32(m == 0));
+		}
+	} else {
+		if (m & 0x00001000) != 0 {
+			m += 0x00002000;
+			if (m & 0x00800000) != 0 {
+				m = 0;
+				e += 1;
+			}
+		}
+
+		if (e > 30) {
+			f := 1e12;
+			for j := 0; j < 10; j += 1 {
+				/* NOTE(bill): Cause overflow */
+				g := intrinsics.volatile_load(&f);
+				g *= g;
+				intrinsics.volatile_store(&f, g);
+			}
+
+			return u16(s | 0x7c00);
+		}
+
+		return u16(s | (e << 10) | (m >> 13));
+	}
+}
+
+
+@(link_name="__truncdfhf2")
+truncdfhf2 :: proc "c" (value: f64) -> u16 {
+	return truncsfhf2(f32(value));
+}
+
+@(link_name="__gnu_h2f_ieee")
+gnu_h2f_ieee :: proc "c" (value: u16) -> f32 {
+	fp32 :: struct #raw_union { u: u32, f: f32 };
+
+	v: fp32;
+	magic, inf_or_nan: fp32;
+	magic.u = u32((254 - 15) << 23);
+	inf_or_nan.u = u32((127 + 16) << 23);
+
+	v.u = u32(value & 0x7fff) << 13;
+	v.f *= magic.f;
+	if v.f >= inf_or_nan.f {
+		v.u |= 255 << 23;
+	}
+	v.u |= u32(value & 0x8000) << 16;
+	return v.f;
+}
+
+
+@(link_name="__gnu_f2h_ieee")
+gnu_f2h_ieee :: proc "c" (value: f32) -> u16 {
+	return truncsfhf2(value);
+}

+ 3 - 0
core/strconv/generic_float.odin

@@ -25,6 +25,9 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int)
 	bits: u64;
 	flt: ^Float_Info;
 	switch bit_size {
+	case 16:
+		bits = u64(transmute(u16)f16(val));
+		flt = &_f16_info;
 	case 32:
 		bits = u64(transmute(u32)f32(val));
 		flt = &_f32_info;

+ 23 - 3
src/check_expr.cpp

@@ -1549,14 +1549,16 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ
 		if (out_value) *out_value = v;
 
 		switch (type->Basic.kind) {
-		// case Basic_f16:
+		case Basic_f16:
 		case Basic_f32:
 		case Basic_f64:
 			return true;
 
+		case Basic_f16le:
+		case Basic_f16be:
 		case Basic_f32le:
-		case Basic_f64le:
 		case Basic_f32be:
+		case Basic_f64le:
 		case Basic_f64be:
 			return true;
 
@@ -2775,12 +2777,14 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
 		if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
 		case Basic_complex64:     add_package_dependency(c, "runtime", "quo_complex64");     break;
 		case Basic_complex128:    add_package_dependency(c, "runtime", "quo_complex128");    break;
+		case Basic_quaternion64:  add_package_dependency(c, "runtime", "quo_quaternion64");  break;
 		case Basic_quaternion128: add_package_dependency(c, "runtime", "quo_quaternion128"); break;
 		case Basic_quaternion256: add_package_dependency(c, "runtime", "quo_quaternion256"); break;
 		}
 	} else if (op.kind == Token_Mul || op.kind == Token_MulEq) {
 		Type *bt = base_type(x->type);
 		if (bt->kind == Type_Basic) switch (bt->Basic.kind) {
+		case Basic_quaternion64:  add_package_dependency(c, "runtime", "mul_quaternion64"); break;
 		case Basic_quaternion128: add_package_dependency(c, "runtime", "mul_quaternion128"); break;
 		case Basic_quaternion256: add_package_dependency(c, "runtime", "mul_quaternion256"); break;
 		}
@@ -4495,6 +4499,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			gb_string_free(s);
 			return false;
 		}
+		if (is_type_endian_specific(x.type)) {
+			gbString s = type_to_string(x.type);
+			error(call, "Arguments with a specified endian are not allow, expected a normal floating point, got '%s'", s);
+			gb_string_free(s);
+			return false;
+		}
 
 		if (x.mode == Addressing_Constant && y.mode == Addressing_Constant) {
 			f64 r = exact_value_to_float(x.value).value_float;
@@ -4507,7 +4517,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 		BasicKind kind = core_type(x.type)->Basic.kind;
 		switch (kind) {
-		// case Basic_f16:          operand->type = t_complex32;       break;
+		case Basic_f16:          operand->type = t_complex32;       break;
 		case Basic_f32:          operand->type = t_complex64;       break;
 		case Basic_f64:          operand->type = t_complex128;      break;
 		case Basic_UntypedFloat: operand->type = t_untyped_complex; break;
@@ -4586,6 +4596,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			gb_string_free(s);
 			return false;
 		}
+		if (is_type_endian_specific(x.type)) {
+			gbString s = type_to_string(x.type);
+			error(call, "Arguments with a specified endian are not allow, expected a normal floating point, got '%s'", s);
+			gb_string_free(s);
+			return false;
+		}
 
 		if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) {
 			f64 r = exact_value_to_float(x.value).value_float;
@@ -4600,6 +4616,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 		BasicKind kind = core_type(x.type)->Basic.kind;
 		switch (kind) {
+		case Basic_f16:          operand->type = t_quaternion64;       break;
 		case Basic_f32:          operand->type = t_quaternion128;      break;
 		case Basic_f64:          operand->type = t_quaternion256;      break;
 		case Basic_UntypedFloat: operand->type = t_untyped_quaternion; break;
@@ -4655,8 +4672,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 		BasicKind kind = core_type(x->type)->Basic.kind;
 		switch (kind) {
+		case Basic_complex32:         x->type = t_f16;           break;
 		case Basic_complex64:         x->type = t_f32;           break;
 		case Basic_complex128:        x->type = t_f64;           break;
+		case Basic_quaternion64:      x->type = t_f16;           break;
 		case Basic_quaternion128:     x->type = t_f32;           break;
 		case Basic_quaternion256:     x->type = t_f64;           break;
 		case Basic_UntypedComplex:    x->type = t_untyped_float; break;
@@ -4708,6 +4727,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 		BasicKind kind = core_type(x->type)->Basic.kind;
 		switch (kind) {
+		case Basic_quaternion64:      x->type = t_f16;           break;
 		case Basic_quaternion128:     x->type = t_f32;           break;
 		case Basic_quaternion256:     x->type = t_f64;           break;
 		case Basic_UntypedComplex:    x->type = t_untyped_float; break;

+ 5 - 0
src/checker.cpp

@@ -1769,6 +1769,10 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("fixdfti"),
 		str_lit("floattidf"),
 
+		str_lit("truncsfhf2"),
+		str_lit("truncdfhf2"),
+		str_lit("gnu_h2f_ieee"),
+		str_lit("gnu_f2h_ieee"),
 
 		str_lit("memset"),
 		str_lit("memcpy"),
@@ -1783,6 +1787,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		str_lit("bswap_64"),
 		str_lit("bswap_128"),
 
+		str_lit("bswap_f16"),
 		str_lit("bswap_f32"),
 		str_lit("bswap_f64"),
 	};

+ 23 - 7
src/common.cpp

@@ -763,7 +763,7 @@ i64 prev_pow2(i64 n) {
 	return n - (n >> 1);
 }
 
-i16 f32_to_f16(f32 value) {
+u16 f32_to_f16(f32 value) {
 	union { u32 i; f32 f; } v;
 	i32 i, s, e, m;
 
@@ -776,20 +776,20 @@ i16 f32_to_f16(f32 value) {
 
 
 	if (e <= 0) {
-		if (e < -10) return cast(i16)s;
+		if (e < -10) return cast(u16)s;
 		m = (m | 0x00800000) >> (1 - e);
 
 		if (m & 0x00001000)
 			m += 0x00002000;
 
-		return cast(i16)(s | (m >> 13));
+		return cast(u16)(s | (m >> 13));
 	} else if (e == 0xff - (127 - 15)) {
 		if (m == 0) {
-			return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */
+			return cast(u16)(s | 0x7c00); /* NOTE(bill): infinity */
 		} else {
 			/* NOTE(bill): NAN */
 			m >>= 13;
-			return cast(i16)(s | 0x7c00 | m | (m == 0));
+			return cast(u16)(s | 0x7c00 | m | (m == 0));
 		}
 	} else {
 		if (m & 0x00001000) {
@@ -807,13 +807,29 @@ i16 f32_to_f16(f32 value) {
 				f *= f; /* NOTE(bill): Cause overflow */
 			}
 
-			return cast(i16)(s | 0x7c00);
+			return cast(u16)(s | 0x7c00);
 		}
 
-		return cast(i16)(s | (e << 10) | (m >> 13));
+		return cast(u16)(s | (e << 10) | (m >> 13));
 	}
 }
 
+f32 f16_to_f32(u16 value) {
+	typedef union { u32 u; f32 f; } fp32;
+	fp32 v;
+
+	fp32 magic = {(254u - 15u) << 23};
+	fp32 inf_or_nan = {(127u + 16u) << 23};
+
+	v.u = (value & 0x7fffu) << 13;
+	v.f *= magic.f;
+	if (v.f >= inf_or_nan.f) {
+		v.u |= 255u << 23;
+	}
+	v.u |= (value & 0x8000u) << 16;
+	return v.f;
+}
+
 f64 gb_sqrt(f64 x) {
 	return sqrt(x);
 }

+ 5 - 1
src/exact_value.cpp

@@ -271,7 +271,11 @@ ExactValue exact_value_float_from_string(String string) {
 			}
 		}
 		u64 u = u64_from_string(string);
-		if (digit_count == 8) {
+		if (digit_count == 4) {
+			u16 x = cast(u16)u;
+			f32 f = f16_to_f32(x);
+			return exact_value_float(cast(f64)f);
+		} else if (digit_count == 8) {
 			u32 x = cast(u32)u;
 			f32 f = bit_cast<f32>(x);
 			return exact_value_float(cast(f64)f);

+ 28 - 11
src/ir.cpp

@@ -2052,24 +2052,27 @@ irDebugEncoding ir_debug_encoding_for_basic(BasicKind kind) {
 	case Basic_uintptr:
 		return irDebugBasicEncoding_unsigned;
 
-	// case Basic_f16:
+	case Basic_f16:
 	case Basic_f32:
 	case Basic_f64:
+	case Basic_f16le:
 	case Basic_f32le:
 	case Basic_f64le:
+	case Basic_f16be:
 	case Basic_f32be:
 	case Basic_f64be:
 			return irDebugBasicEncoding_float;
 
-	// case Basic_complex32:
+	case Basic_complex32:
 	case Basic_complex64:
 	case Basic_complex128:
+	case Basic_quaternion64:
+	case Basic_quaternion128:
+	case Basic_quaternion256:
 	case Basic_cstring:
 	case Basic_string:
 	case Basic_any:
 	case Basic_rawptr:
-	case Basic_quaternion128:
-	case Basic_quaternion256:
 		break; // not a "DIBasicType"
 	}
 
@@ -2549,9 +2552,9 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, Type *type, Entity *e, irD
 	if (type->kind == Type_Basic) {
 		switch (type->Basic.kind) {
 		// Composite basic types
-		case Basic_complex64: case Basic_complex128:
+		case Basic_complex32: case Basic_complex64: case Basic_complex128:
 			return ir_add_debug_info_type_complex(module, type);
-		case Basic_quaternion128: case Basic_quaternion256:
+		case Basic_quaternion64: case Basic_quaternion128: case Basic_quaternion256:
 			return ir_add_debug_info_type_quaternion(module, type);
 		case Basic_string:
 			return ir_add_debug_info_type_string(module, scope, e, type);
@@ -5430,7 +5433,9 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) {
 			case 1: result_type = t_typeid; break;
 			}
 			break;
-		case Basic_complex64: case Basic_complex128:
+		case Basic_complex32:
+		case Basic_complex64:
+		case Basic_complex128:
 		{
 			Type *ft = base_complex_elem_type(t);
 			switch (index) {
@@ -5439,7 +5444,9 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) {
 			}
 			break;
 		}
-		case Basic_quaternion128: case Basic_quaternion256:
+		case Basic_quaternion64:
+		case Basic_quaternion128:
+		case Basic_quaternion256:
 		{
 			Type *ft = base_complex_elem_type(t);
 			switch (index) {
@@ -5828,6 +5835,7 @@ irValue *ir_emit_byte_swap(irProcedure *proc, irValue *value, Type *t) {
 
 		char const *proc_name = nullptr;
 		switch (sz*8) {
+		case 16: proc_name  = "bswap_f16"; break;
 		case 32: proc_name  = "bswap_f32"; break;
 		case 64: proc_name  = "bswap_f64"; break;
 		}
@@ -6272,9 +6280,10 @@ bool ir_is_type_aggregate(Type *t) {
 		case Basic_any:
 			return true;
 
-		// case Basic_complex32:
+		case Basic_complex32:
 		case Basic_complex64:
 		case Basic_complex128:
+		case Basic_quaternion64:
 		case Basic_quaternion128:
 		case Basic_quaternion256:
 			return true;
@@ -7025,6 +7034,7 @@ irValue *ir_emit_min(irProcedure *proc, Type *t, irValue *x, irValue *y) {
 		args[0] = x;
 		args[1] = y;
 		switch (sz) {
+		case 16: return ir_emit_runtime_call(proc, "min_f16", args);
 		case 32: return ir_emit_runtime_call(proc, "min_f32", args);
 		case 64: return ir_emit_runtime_call(proc, "min_f64", args);
 		}
@@ -7043,6 +7053,7 @@ irValue *ir_emit_max(irProcedure *proc, Type *t, irValue *x, irValue *y) {
 		args[0] = x;
 		args[1] = y;
 		switch (sz) {
+		case 16: return ir_emit_runtime_call(proc, "max_f16", args);
 		case 32: return ir_emit_runtime_call(proc, "max_f32", args);
 		case 64: return ir_emit_runtime_call(proc, "max_f64", args);
 		}
@@ -7487,6 +7498,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
 			auto args = array_make<irValue *>(ir_allocator(), 1);
 			args[0] = x;
 			switch (sz) {
+			case 64:  return ir_emit_runtime_call(proc, "abs_quaternion64", args);
 			case 128: return ir_emit_runtime_call(proc, "abs_quaternion128", args);
 			case 256: return ir_emit_runtime_call(proc, "abs_quaternion256", args);
 			}
@@ -7496,6 +7508,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
 			auto args = array_make<irValue *>(ir_allocator(), 1);
 			args[0] = x;
 			switch (sz) {
+			case 32:  return ir_emit_runtime_call(proc, "abs_complex32",  args);
 			case 64:  return ir_emit_runtime_call(proc, "abs_complex64",  args);
 			case 128: return ir_emit_runtime_call(proc, "abs_complex128", args);
 			}
@@ -7505,6 +7518,7 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
 			auto args = array_make<irValue *>(ir_allocator(), 1);
 			args[0] = x;
 			switch (sz) {
+			case 16: return ir_emit_runtime_call(proc, "abs_f16", args);
 			case 32: return ir_emit_runtime_call(proc, "abs_f32", args);
 			case 64: return ir_emit_runtime_call(proc, "abs_f64", args);
 			}
@@ -12162,11 +12176,13 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 				tag = ir_emit_conv(proc, variant_ptr, t_type_info_rune_ptr);
 				break;
 
-			// case Basic_f16:
+			case Basic_f16:
 			case Basic_f32:
 			case Basic_f64:
+			case Basic_f16le:
 			case Basic_f32le:
 			case Basic_f64le:
+			case Basic_f16be:
 			case Basic_f32be:
 			case Basic_f64be:
 				{
@@ -12185,12 +12201,13 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info
 				break;
 
 
-			// case Basic_complex32:
+			case Basic_complex32:
 			case Basic_complex64:
 			case Basic_complex128:
 				tag = ir_emit_conv(proc, variant_ptr, t_type_info_complex_ptr);
 				break;
 
+			case Basic_quaternion64:
 			case Basic_quaternion128:
 			case Basic_quaternion256:
 				tag = ir_emit_conv(proc, variant_ptr, t_type_info_quaternion_ptr);

+ 24 - 4
src/ir_print.cpp

@@ -418,21 +418,24 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
 			}
 			return;
 
-		// case Basic_f16:    ir_write_str_lit(f, "half");                 return;
-		case Basic_f32:    ir_write_str_lit(f, "float");                   return;
-		case Basic_f64:    ir_write_str_lit(f, "double");                  return;
+		case Basic_f16:    ir_write_str_lit(f, "half");     return;
+		case Basic_f32:    ir_write_str_lit(f, "float");    return;
+		case Basic_f64:    ir_write_str_lit(f, "double");   return;
 
 
+		case Basic_f16le:    ir_write_str_lit(f, "half");   return;
 		case Basic_f32le:    ir_write_str_lit(f, "float");  return;
 		case Basic_f64le:    ir_write_str_lit(f, "double"); return;
 
+		case Basic_f16be:    ir_write_str_lit(f, "half");    return;
 		case Basic_f32be:    ir_write_str_lit(f, "float");   return;
 		case Basic_f64be:    ir_write_str_lit(f, "double");  return;
 
-		// case Basic_complex32:  ir_write_str_lit(f, "%%..complex32");    return;
+		case Basic_complex32:  ir_write_str_lit(f, "%%..complex32");       return;
 		case Basic_complex64:  ir_write_str_lit(f, "%..complex64");        return;
 		case Basic_complex128: ir_write_str_lit(f, "%..complex128");       return;
 
+		case Basic_quaternion64:  ir_write_str_lit(f, "%..quaternion64");  return;
 		case Basic_quaternion128: ir_write_str_lit(f, "%..quaternion128"); return;
 		case Basic_quaternion256: ir_write_str_lit(f, "%..quaternion256"); return;
 
@@ -873,6 +876,23 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
 		}
 	#else
 		switch (type->Basic.kind) {
+		case Basic_f16:
+			ir_fprintf(f, "fptrunc (float bitcast (i32 %u to float) to half)", u_32);
+			break;
+		case Basic_f16le:
+			if (build_context.endian_kind != TargetEndian_Little) {
+				u_32 = gb_endian_swap32(u_32);
+			}
+			ir_fprintf(f, "fptrunc (float bitcast (i32 %u to float) to half)", u_32);
+			break;
+		case Basic_f16be:
+			if (build_context.endian_kind != TargetEndian_Big) {
+				u_32 = gb_endian_swap32(u_32);
+			}
+			ir_fprintf(f, "fptrunc (float bitcast (i32 %u to float) to half)", u_32);
+			break;
+
+
 		case Basic_f32:
 			ir_fprintf(f, "bitcast (i32 %u to float)", u_32);
 			break;

+ 8 - 0
src/llvm_abi.cpp

@@ -158,6 +158,8 @@ i64 lb_sizeof(LLVMTypeRef type) {
 			unsigned w = LLVMGetIntTypeWidth(type);
 			return (w + 7)/8;
 		}
+	case LLVMHalfTypeKind:
+		return 2;
 	case LLVMFloatTypeKind:
 		return 4;
 	case LLVMDoubleTypeKind:
@@ -222,6 +224,8 @@ i64 lb_alignof(LLVMTypeRef type) {
 			unsigned w = LLVMGetIntTypeWidth(type);
 			return gb_clamp((w + 7)/8, 1, build_context.max_align);
 		}
+	case LLVMHalfTypeKind:
+		return 2;
 	case LLVMFloatTypeKind:
 		return 4;
 	case LLVMDoubleTypeKind:
@@ -584,6 +588,7 @@ namespace lbAbiAmd64SysV {
 		LLVMTypeKind kind = LLVMGetTypeKind(type);
 		switch (kind) {
 		case LLVMIntegerTypeKind:
+		case LLVMHalfTypeKind:
 		case LLVMFloatTypeKind:
 		case LLVMDoubleTypeKind:
 		case LLVMPointerTypeKind:
@@ -799,6 +804,7 @@ namespace lbAbiAmd64SysV {
 		switch (LLVMGetTypeKind(t)) {
 		case LLVMIntegerTypeKind:
 		case LLVMPointerTypeKind:
+		case LLVMHalfTypeKind:
 			unify(cls, ix + off/8, RegClass_Int);
 			break;
 		case LLVMFloatTypeKind:
@@ -842,6 +848,7 @@ namespace lbAbiAmd64SysV {
 				RegClass reg = RegClass_NoClass;
 				switch (elem_kind) {
 				case LLVMIntegerTypeKind:
+				case LLVMHalfTypeKind:
 					switch (LLVMGetIntTypeWidth(elem)) {
 					case 8:  reg = RegClass_SSEInt8;
 					case 16: reg = RegClass_SSEInt16;
@@ -934,6 +941,7 @@ namespace lbAbiArm64 {
 		LLVMTypeKind kind = LLVMGetTypeKind(type);
 		switch (kind) {
 		case LLVMIntegerTypeKind:
+		case LLVMHalfTypeKind:
 		case LLVMFloatTypeKind:
 		case LLVMDoubleTypeKind:
 		case LLVMPointerTypeKind:

+ 97 - 14
src/llvm_backend.cpp

@@ -961,17 +961,34 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 
 		case Basic_rune: return LLVMInt32TypeInContext(ctx);
 
-		// Basic_f16,
+
+		case Basic_f16: return LLVMHalfTypeInContext(ctx);
 		case Basic_f32: return LLVMFloatTypeInContext(ctx);
 		case Basic_f64: return LLVMDoubleTypeInContext(ctx);
 
+		case Basic_f16le: return LLVMHalfTypeInContext(ctx);
 		case Basic_f32le: return LLVMFloatTypeInContext(ctx);
 		case Basic_f64le: return LLVMDoubleTypeInContext(ctx);
 
+		case Basic_f16be: return LLVMHalfTypeInContext(ctx);
 		case Basic_f32be: return LLVMFloatTypeInContext(ctx);
 		case Basic_f64be: return LLVMDoubleTypeInContext(ctx);
 
-		// Basic_complex32,
+		case Basic_complex32:
+			{
+				char const *name = "..complex32";
+				LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
+				if (type != nullptr) {
+					return type;
+				}
+				type = LLVMStructCreateNamed(ctx, name);
+				LLVMTypeRef fields[2] = {
+					lb_type(m, t_f16),
+					lb_type(m, t_f16),
+				};
+				LLVMStructSetBody(type, fields, 2, false);
+				return type;
+			}
 		case Basic_complex64:
 			{
 				char const *name = "..complex64";
@@ -1003,6 +1020,23 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
 				return type;
 			}
 
+		case Basic_quaternion64:
+			{
+				char const *name = "..quaternion64";
+				LLVMTypeRef type = LLVMGetTypeByName(m->mod, name);
+				if (type != nullptr) {
+					return type;
+				}
+				type = LLVMStructCreateNamed(ctx, name);
+				LLVMTypeRef fields[4] = {
+					lb_type(m, t_f16),
+					lb_type(m, t_f16),
+					lb_type(m, t_f16),
+					lb_type(m, t_f16),
+				};
+				LLVMStructSetBody(type, fields, 4, false);
+				return type;
+			}
 		case Basic_quaternion128:
 			{
 				char const *name = "..quaternion128";
@@ -1622,7 +1656,8 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 
 		case Basic_rune: return lb_debug_type_basic_type(m, str_lit("rune"), 32, LLVMDWARFTypeEncoding_Utf);
 
-		// Basic_f16,
+
+		case Basic_f16: return lb_debug_type_basic_type(m, str_lit("f16"), 16, LLVMDWARFTypeEncoding_Float);
 		case Basic_f32: return lb_debug_type_basic_type(m, str_lit("f32"), 32, LLVMDWARFTypeEncoding_Float);
 		case Basic_f64: return lb_debug_type_basic_type(m, str_lit("f64"), 64, LLVMDWARFTypeEncoding_Float);
 
@@ -1642,6 +1677,8 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 		case Basic_u64le:  return lb_debug_type_basic_type(m, str_lit("u64le"),  64,  LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
 		case Basic_i128le: return lb_debug_type_basic_type(m, str_lit("i128le"), 128, LLVMDWARFTypeEncoding_Signed,   LLVMDIFlagLittleEndian);
 		case Basic_u128le: return lb_debug_type_basic_type(m, str_lit("u128le"), 128, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagLittleEndian);
+
+		case Basic_f16le: return lb_debug_type_basic_type(m,  str_lit("f16le"),   16, LLVMDWARFTypeEncoding_Float,    LLVMDIFlagLittleEndian);
 		case Basic_f32le: return lb_debug_type_basic_type(m,  str_lit("f32le"),   32, LLVMDWARFTypeEncoding_Float,    LLVMDIFlagLittleEndian);
 		case Basic_f64le: return lb_debug_type_basic_type(m,  str_lit("f64le"),   64, LLVMDWARFTypeEncoding_Float,    LLVMDIFlagLittleEndian);
 
@@ -1653,10 +1690,18 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 		case Basic_u64be:  return lb_debug_type_basic_type(m, str_lit("u64be"),  64,  LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
 		case Basic_i128be: return lb_debug_type_basic_type(m, str_lit("i128be"), 128, LLVMDWARFTypeEncoding_Signed,   LLVMDIFlagBigEndian);
 		case Basic_u128be: return lb_debug_type_basic_type(m, str_lit("u128be"), 128, LLVMDWARFTypeEncoding_Unsigned, LLVMDIFlagBigEndian);
+
+		case Basic_f16be: return lb_debug_type_basic_type(m,  str_lit("f16be"),   16, LLVMDWARFTypeEncoding_Float,    LLVMDIFlagLittleEndian);
 		case Basic_f32be: return lb_debug_type_basic_type(m,  str_lit("f32be"),   32, LLVMDWARFTypeEncoding_Float,    LLVMDIFlagLittleEndian);
 		case Basic_f64be: return lb_debug_type_basic_type(m,  str_lit("f64be"),   64, LLVMDWARFTypeEncoding_Float,    LLVMDIFlagLittleEndian);
 
-		// Basic_complex32,
+		case Basic_complex32:
+			{
+				LLVMMetadataRef elements[2] = {};
+				elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0);
+				elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 4);
+				return lb_debug_basic_struct(m, str_lit("complex32"), 64, 32, elements, gb_count_of(elements));
+			}
 		case Basic_complex64:
 			{
 				LLVMMetadataRef elements[2] = {};
@@ -1672,6 +1717,15 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
 				return lb_debug_basic_struct(m, str_lit("complex128"), 128, 64, elements, gb_count_of(elements));
 			}
 
+		case Basic_quaternion64:
+			{
+				LLVMMetadataRef elements[4] = {};
+				elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0);
+				elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 4);
+				elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 8);
+				elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 12);
+				return lb_debug_basic_struct(m, str_lit("quaternion64"), 128, 32, elements, gb_count_of(elements));
+			}
 		case Basic_quaternion128:
 			{
 				LLVMMetadataRef elements[4] = {};
@@ -5262,6 +5316,17 @@ lbValue lb_const_bool(lbModule *m, Type *type, bool value) {
 	return res;
 }
 
+LLVMValueRef lb_const_f16(lbModule *m, f32 f, Type *type=t_f16) {
+	GB_ASSERT(type_size_of(type) == 2);
+
+	u16 u = f32_to_f16(f);
+	if (is_type_different_to_arch_endianness(type)) {
+		u = gb_endian_swap16(u);
+	}
+	LLVMValueRef i = LLVMConstInt(LLVMInt16TypeInContext(m->ctx), u, false);
+	return LLVMConstBitCast(i, lb_type(m, type));
+}
+
 LLVMValueRef lb_const_f32(lbModule *m, f32 f, Type *type=t_f32) {
 	GB_ASSERT(type_size_of(type) == 4);
 	u32 u = bit_cast<u32>(f);
@@ -5761,11 +5826,6 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
 		}
 		return res;
 	case ExactValue_Float:
-		if (type_size_of(type) == 4) {
-			f32 f = cast(f32)value.value_float;
-			res.value = lb_const_f32(m, f, type);
-			return res;
-		}
 		if (is_type_different_to_arch_endianness(type)) {
 			u64 u = bit_cast<u64>(value.value_float);
 			u = gb_endian_swap64(u);
@@ -5778,6 +5838,10 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
 		{
 			LLVMValueRef values[2] = {};
 			switch (8*type_size_of(type)) {
+			case 32:
+				values[0] = lb_const_f16(m, cast(f32)value.value_complex->real);
+				values[1] = lb_const_f16(m, cast(f32)value.value_complex->imag);
+				break;
 			case 64:
 				values[0] = lb_const_f32(m, cast(f32)value.value_complex->real);
 				values[1] = lb_const_f32(m, cast(f32)value.value_complex->imag);
@@ -5796,6 +5860,13 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
 		{
 			LLVMValueRef values[4] = {};
 			switch (8*type_size_of(type)) {
+			case 64:
+				// @QuaternionLayout
+				values[3] = lb_const_f16(m, cast(f32)value.value_quaternion->real);
+				values[0] = lb_const_f16(m, cast(f32)value.value_quaternion->imag);
+				values[1] = lb_const_f16(m, cast(f32)value.value_quaternion->jmag);
+				values[2] = lb_const_f16(m, cast(f32)value.value_quaternion->kmag);
+				break;
 			case 128:
 				// @QuaternionLayout
 				values[3] = lb_const_f32(m, cast(f32)value.value_quaternion->real);
@@ -7340,9 +7411,10 @@ bool lb_is_type_aggregate(Type *t) {
 		case Basic_any:
 			return true;
 
-		// case Basic_complex32:
+		case Basic_complex32:
 		case Basic_complex64:
 		case Basic_complex128:
+		case Basic_quaternion64:
 		case Basic_quaternion128:
 		case Basic_quaternion256:
 			return true;
@@ -7663,7 +7735,9 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
 			case 1: result_type = t_typeid; break;
 			}
 			break;
-		case Basic_complex64: case Basic_complex128:
+		case Basic_complex32:
+		case Basic_complex64:
+		case Basic_complex128:
 		{
 			Type *ft = base_complex_elem_type(t);
 			switch (index) {
@@ -7672,7 +7746,9 @@ lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
 			}
 			break;
 		}
-		case Basic_quaternion128: case Basic_quaternion256:
+		case Basic_quaternion64:
+		case Basic_quaternion128:
+		case Basic_quaternion256:
 		{
 			Type *ft = base_complex_elem_type(t);
 			switch (index) {
@@ -8816,6 +8892,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			auto args = array_make<lbValue>(permanent_allocator(), 1);
 			args[0] = x;
 			switch (sz) {
+			case 64:  return lb_emit_runtime_call(p, "abs_quaternion64", args);
 			case 128: return lb_emit_runtime_call(p, "abs_quaternion128", args);
 			case 256: return lb_emit_runtime_call(p, "abs_quaternion256", args);
 			}
@@ -8825,6 +8902,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			auto args = array_make<lbValue>(permanent_allocator(), 1);
 			args[0] = x;
 			switch (sz) {
+			case 32:  return lb_emit_runtime_call(p, "abs_complex32",  args);
 			case 64:  return lb_emit_runtime_call(p, "abs_complex64",  args);
 			case 128: return lb_emit_runtime_call(p, "abs_complex128", args);
 			}
@@ -8834,6 +8912,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			auto args = array_make<lbValue>(permanent_allocator(), 1);
 			args[0] = x;
 			switch (sz) {
+			case 16: return lb_emit_runtime_call(p, "abs_f16", args);
 			case 32: return lb_emit_runtime_call(p, "abs_f32", args);
 			case 64: return lb_emit_runtime_call(p, "abs_f64", args);
 			}
@@ -9566,6 +9645,7 @@ lbValue lb_emit_byte_swap(lbProcedure *p, lbValue value, Type *platform_type) {
 		if (is_type_float(platform_type)) {
 			String name = {};
 			switch (sz) {
+			case 2:  name = str_lit("bswap_f16");  break;
 			case 4:  name = str_lit("bswap_f32");  break;
 			case 8:  name = str_lit("bswap_f64");  break;
 			default: GB_PANIC("unhandled byteswap size"); break;
@@ -12678,11 +12758,13 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_rune_ptr);
 				break;
 
-			// case Basic_f16:
+			case Basic_f16:
 			case Basic_f32:
 			case Basic_f64:
+			case Basic_f16le:
 			case Basic_f32le:
 			case Basic_f64le:
+			case Basic_f16be:
 			case Basic_f32be:
 			case Basic_f64be:
 				{
@@ -12708,12 +12790,13 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
 				}
 				break;
 
-			// case Basic_complex32:
+			case Basic_complex32:
 			case Basic_complex64:
 			case Basic_complex128:
 				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_complex_ptr);
 				break;
 
+			case Basic_quaternion64:
 			case Basic_quaternion128:
 			case Basic_quaternion256:
 				tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_quaternion_ptr);

+ 2 - 1
src/tokenizer.cpp

@@ -865,11 +865,12 @@ void scan_number_to_token(Tokenizer *t, Token *token, bool seen_decimal_point) {
 					}
 				}
 				switch (digit_count) {
+				case 4:
 				case 8:
 				case 16:
 					break;
 				default:
-					tokenizer_err(t, "Invalid hexadecimal float, expected 8 or 16 digits, got %td", digit_count);
+					tokenizer_err(t, "Invalid hexadecimal float, expected 4, 8, or 16 digits, got %td", digit_count);
 					break;
 				}
 			}

+ 92 - 33
src/types.cpp

@@ -25,14 +25,15 @@ enum BasicKind {
 
 	Basic_rune,
 
-	// Basic_f16,
+	Basic_f16,
 	Basic_f32,
 	Basic_f64,
 
-	// Basic_complex32,
+	Basic_complex32,
 	Basic_complex64,
 	Basic_complex128,
 
+	Basic_quaternion64,
 	Basic_quaternion128,
 	Basic_quaternion256,
 
@@ -65,9 +66,11 @@ enum BasicKind {
 	Basic_i128be,
 	Basic_u128be,
 
+	Basic_f16le,
 	Basic_f32le,
 	Basic_f64le,
 
+	Basic_f16be,
 	Basic_f32be,
 	Basic_f64be,
 
@@ -463,14 +466,15 @@ gb_global Type basic_types[] = {
 
 	{Type_Basic, {Basic_rune,              BasicFlag_Integer | BasicFlag_Rune,         4, STR_LIT("rune")}},
 
-	// {Type_Basic, {Basic_f16,               BasicFlag_Float,                            2, STR_LIT("f16")}},
+	{Type_Basic, {Basic_f16,               BasicFlag_Float,                            2, STR_LIT("f16")}},
 	{Type_Basic, {Basic_f32,               BasicFlag_Float,                            4, STR_LIT("f32")}},
 	{Type_Basic, {Basic_f64,               BasicFlag_Float,                            8, STR_LIT("f64")}},
 
-	// {Type_Basic, {Basic_complex32,         BasicFlag_Complex,                          4, STR_LIT("complex32")}},
+	{Type_Basic, {Basic_complex32,         BasicFlag_Complex,                          4, STR_LIT("complex32")}},
 	{Type_Basic, {Basic_complex64,         BasicFlag_Complex,                          8, STR_LIT("complex64")}},
 	{Type_Basic, {Basic_complex128,        BasicFlag_Complex,                         16, STR_LIT("complex128")}},
 
+	{Type_Basic, {Basic_quaternion64,      BasicFlag_Quaternion,                       8, STR_LIT("quaternion64")}},
 	{Type_Basic, {Basic_quaternion128,     BasicFlag_Quaternion,                      16, STR_LIT("quaternion128")}},
 	{Type_Basic, {Basic_quaternion256,     BasicFlag_Quaternion,                      32, STR_LIT("quaternion256")}},
 
@@ -504,9 +508,11 @@ gb_global Type basic_types[] = {
 	{Type_Basic, {Basic_i128be, BasicFlag_Integer                      | BasicFlag_EndianBig,    16, STR_LIT("i128be")}},
 	{Type_Basic, {Basic_u128be, BasicFlag_Integer | BasicFlag_Unsigned | BasicFlag_EndianBig,    16, STR_LIT("u128be")}},
 
+	{Type_Basic, {Basic_f16le, BasicFlag_Float | BasicFlag_EndianLittle, 2, STR_LIT("f16le")}},
 	{Type_Basic, {Basic_f32le, BasicFlag_Float | BasicFlag_EndianLittle, 4, STR_LIT("f32le")}},
 	{Type_Basic, {Basic_f64le, BasicFlag_Float | BasicFlag_EndianLittle, 8, STR_LIT("f64le")}},
 
+	{Type_Basic, {Basic_f16be, BasicFlag_Float | BasicFlag_EndianBig,    2, STR_LIT("f16be")}},
 	{Type_Basic, {Basic_f32be, BasicFlag_Float | BasicFlag_EndianBig,    4, STR_LIT("f32be")}},
 	{Type_Basic, {Basic_f64be, BasicFlag_Float | BasicFlag_EndianBig,    8, STR_LIT("f64be")}},
 
@@ -543,14 +549,15 @@ gb_global Type *t_u128            = &basic_types[Basic_u128];
 
 gb_global Type *t_rune            = &basic_types[Basic_rune];
 
-// gb_global Type *t_f16             = &basic_types[Basic_f16];
+gb_global Type *t_f16             = &basic_types[Basic_f16];
 gb_global Type *t_f32             = &basic_types[Basic_f32];
 gb_global Type *t_f64             = &basic_types[Basic_f64];
 
-// gb_global Type *t_complex32       = &basic_types[Basic_complex32];
+gb_global Type *t_complex32       = &basic_types[Basic_complex32];
 gb_global Type *t_complex64       = &basic_types[Basic_complex64];
 gb_global Type *t_complex128      = &basic_types[Basic_complex128];
 
+gb_global Type *t_quaternion64    = &basic_types[Basic_quaternion64];
 gb_global Type *t_quaternion128   = &basic_types[Basic_quaternion128];
 gb_global Type *t_quaternion256   = &basic_types[Basic_quaternion256];
 
@@ -1127,6 +1134,13 @@ bool is_type_quaternion(Type *t) {
 	}
 	return false;
 }
+bool is_type_f16(Type *t) {
+	t = core_type(t);
+	if (t->kind == Type_Basic) {
+		return t->Basic.kind == Basic_f16;
+	}
+	return false;
+}
 bool is_type_f32(Type *t) {
 	t = core_type(t);
 	if (t->kind == Type_Basic) {
@@ -1265,7 +1279,7 @@ Type *core_array_type(Type *t) {
 	for (;;) {
 		Type *prev = t;
 		t = base_array_type(t);
-		if (t->kind != Type_Array && t->kind != Type_SimdVector) {
+		if (t->kind != Type_Array && t->kind != Type_EnumeratedArray && t->kind != Type_SimdVector) {
 			break;
 		}
 	}
@@ -1278,9 +1292,10 @@ Type *base_complex_elem_type(Type *t) {
 	t = core_type(t);
 	if (t->kind == Type_Basic) {
 		switch (t->Basic.kind) {
-		// case Basic_complex32:         return t_f16;
+		case Basic_complex32:         return t_f16;
 		case Basic_complex64:         return t_f32;
 		case Basic_complex128:        return t_f64;
+		case Basic_quaternion64:      return t_f16;
 		case Basic_quaternion128:     return t_f32;
 		case Basic_quaternion256:     return t_f64;
 		case Basic_UntypedComplex:    return t_untyped_float;
@@ -1392,7 +1407,43 @@ bool is_type_endian_little(Type *t) {
 bool types_have_same_internal_endian(Type *a, Type *b) {
 	return is_type_endian_little(a) == is_type_endian_little(b);
 }
+bool is_type_endian_specific(Type *t) {
+	t = core_type(t);
+	if (t->kind == Type_BitSet) {
+		t = bit_set_to_int(t);
+	}
+	if (t->kind == Type_Basic) {
+		switch (t->Basic.kind) {
+		case Basic_i16le:
+		case Basic_u16le:
+		case Basic_i32le:
+		case Basic_u32le:
+		case Basic_i64le:
+		case Basic_u64le:
+		case Basic_u128le:
+			return true;
+
+		case Basic_i16be:
+		case Basic_u16be:
+		case Basic_i32be:
+		case Basic_u32be:
+		case Basic_i64be:
+		case Basic_u64be:
+		case Basic_u128be:
+			return true;
 
+		case Basic_f16le:
+		case Basic_f16be:
+		case Basic_f32le:
+		case Basic_f32be:
+		case Basic_f64le:
+		case Basic_f64be:
+			return true;
+		}
+	}
+
+	return false;
+}
 
 bool is_type_dereferenceable(Type *t) {
 	if (is_type_rawptr(t)) {
@@ -1428,6 +1479,7 @@ Type *integer_endian_type_to_platform_type(Type *t) {
 	case Basic_u32le: return t_u32;
 	case Basic_i64le: return t_i64;
 	case Basic_u64le: return t_u64;
+	case Basic_u128le: return t_u128;
 
 	case Basic_i16be: return t_i16;
 	case Basic_u16be: return t_u16;
@@ -1435,7 +1487,10 @@ Type *integer_endian_type_to_platform_type(Type *t) {
 	case Basic_u32be: return t_u32;
 	case Basic_i64be: return t_i64;
 	case Basic_u64be: return t_u64;
+	case Basic_u128be: return t_u128;
 
+	case Basic_f16le: return t_f16;
+	case Basic_f16be: return t_f16;
 	case Basic_f32le: return t_f32;
 	case Basic_f32be: return t_f32;
 	case Basic_f64le: return t_f64;
@@ -1484,31 +1539,6 @@ bool is_type_valid_for_keys(Type *t) {
 		return false;
 	}
 	return is_type_comparable(t);
-#if 0
-	if (is_type_integer(t)) {
-		return true;
-	}
-	if (is_type_float(t)) {
-		return true;
-	}
-	if (is_type_string(t)) {
-		return true;
-	}
-	if (is_type_pointer(t)) {
-		return true;
-	}
-	if (is_type_typeid(t)) {
-		return true;
-	}
-	if (is_type_simple_compare(t)) {
-		return true;
-	}
-	if (is_type_comparable(t)) {
-		return true;
-	}
-
-	return false;
-#endif
 }
 
 bool is_type_valid_bit_set_elem(Type *t) {
@@ -2493,6 +2523,35 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty
 		#endif
 		} break;
 
+		case Basic_quaternion64: {
+			// @QuaternionLayout
+			gb_local_persist String w = str_lit("w");
+			gb_local_persist String x = str_lit("x");
+			gb_local_persist String y = str_lit("y");
+			gb_local_persist String z = str_lit("z");
+			gb_local_persist Entity *entity__w = alloc_entity_field(nullptr, make_token_ident(w), t_f32, false, 3);
+			gb_local_persist Entity *entity__x = alloc_entity_field(nullptr, make_token_ident(x), t_f32, false, 0);
+			gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f32, false, 1);
+			gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f32, false, 2);
+			if (field_name == w) {
+				selection_add_index(&sel, 3);
+				sel.entity = entity__w;
+				return sel;
+			} else if (field_name == x) {
+				selection_add_index(&sel, 0);
+				sel.entity = entity__x;
+				return sel;
+			} else if (field_name == y) {
+				selection_add_index(&sel, 1);
+				sel.entity = entity__y;
+				return sel;
+			} else if (field_name == z) {
+				selection_add_index(&sel, 2);
+				sel.entity = entity__z;
+				return sel;
+			}
+		} break;
+
 		case Basic_quaternion128: {
 			// @QuaternionLayout
 			gb_local_persist String w = str_lit("w");