Browse Source

Pointer arithmetic builtin procedures

Ginger Bill 9 years ago
parent
commit
ae75ab169b
6 changed files with 391 additions and 143 deletions
  1. 10 0
      src/checker/checker.cpp
  2. 5 3
      src/checker/entity.cpp
  3. 303 30
      src/checker/expr.cpp
  4. 0 96
      src/checker/stmt.cpp
  5. 71 14
      src/codegen/ssa.cpp
  6. 2 0
      src/unicode.cpp

+ 10 - 0
src/checker/checker.cpp

@@ -120,12 +120,18 @@ enum BuiltinProcId {
 	BuiltinProc_offset_of,
 	BuiltinProc_offset_of,
 	BuiltinProc_offset_of_val,
 	BuiltinProc_offset_of_val,
 	BuiltinProc_static_assert,
 	BuiltinProc_static_assert,
+
 	BuiltinProc_len,
 	BuiltinProc_len,
 	BuiltinProc_cap,
 	BuiltinProc_cap,
 	BuiltinProc_copy,
 	BuiltinProc_copy,
 	BuiltinProc_append,
 	BuiltinProc_append,
+
 	BuiltinProc_swizzle,
 	BuiltinProc_swizzle,
 
 
+	BuiltinProc_ptr_offset,
+	BuiltinProc_ptr_sub,
+	BuiltinProc_slice_ptr,
+
 	BuiltinProc_Count,
 	BuiltinProc_Count,
 };
 };
 struct BuiltinProc {
 struct BuiltinProc {
@@ -151,6 +157,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 	{STR_LIT("append"),           2, false, Expression_Expression},
 	{STR_LIT("append"),           2, false, Expression_Expression},
 
 
 	{STR_LIT("swizzle"),          1, true,  Expression_Expression},
 	{STR_LIT("swizzle"),          1, true,  Expression_Expression},
+
+	{STR_LIT("ptr_offset"),       2, false, Expression_Expression},
+	{STR_LIT("ptr_sub"),          2, false, Expression_Expression},
+	{STR_LIT("slice_ptr"),        2, true,  Expression_Expression},
 };
 };
 
 
 struct CheckerContext {
 struct CheckerContext {

+ 5 - 3
src/checker/entity.cpp

@@ -39,11 +39,13 @@ struct Entity {
 		struct { ExactValue value; } Constant;
 		struct { ExactValue value; } Constant;
 		struct {
 		struct {
 			b8 visited;   // Cycle detection
 			b8 visited;   // Cycle detection
-			b8 is_field;  // Is struct field
 			b8 used;      // Variable is used
 			b8 used;      // Variable is used
-			b8 anonymous; // Variable is an anonymous struct field
+			b8 is_field;  // Is struct field
+			b8 anonymous; // Variable is an anonymous
 		} Variable;
 		} Variable;
-		struct { b8 used; } Procedure;
+		struct {
+			b8 used;
+		} Procedure;
 		struct { BuiltinProcId id; } Builtin;
 		struct { BuiltinProcId id; } Builtin;
 	};
 	};
 };
 };

+ 303 - 30
src/checker/expr.cpp

@@ -1,17 +1,124 @@
-void           check_assignment        (Checker *c, Operand *operand, Type *type, String context_name);
-b32            check_is_assignable_to  (Checker *c, Operand *operand, Type *type);
-void           check_expr              (Checker *c, Operand *operand, AstNode *expression);
-void           check_multi_expr        (Checker *c, Operand *operand, AstNode *expression);
-void           check_expr_or_type      (Checker *c, Operand *operand, AstNode *expression);
-ExpressionKind check_expr_base         (Checker *c, Operand *operand, AstNode *expression, Type *type_hint = NULL);
-Type *         check_type              (Checker *c, AstNode *expression, Type *named_type = NULL, CycleChecker *cycle_checker = NULL);
-void           check_selector          (Checker *c, Operand *operand, AstNode *node);
-void           check_not_tuple         (Checker *c, Operand *operand);
-void           convert_to_typed        (Checker *c, Operand *operand, Type *target_type);
-gbString       expr_to_string          (AstNode *expression);
-void           check_entity_decl       (Checker *c, Entity *e, DeclInfo *decl, Type *named_type, CycleChecker *cycle_checker = NULL);
-void           check_proc_body         (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
-void           update_expr_type        (Checker *c, AstNode *e, Type *type, b32 final);
+void           check_expr                (Checker *c, Operand *operand, AstNode *expression);
+void           check_multi_expr          (Checker *c, Operand *operand, AstNode *expression);
+void           check_expr_or_type        (Checker *c, Operand *operand, AstNode *expression);
+ExpressionKind check_expr_base           (Checker *c, Operand *operand, AstNode *expression, Type *type_hint = NULL);
+Type *         check_type                (Checker *c, AstNode *expression, Type *named_type = NULL, CycleChecker *cycle_checker = NULL);
+void           check_selector            (Checker *c, Operand *operand, AstNode *node);
+void           check_not_tuple           (Checker *c, Operand *operand);
+b32            check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
+void           convert_to_typed          (Checker *c, Operand *operand, Type *target_type);
+gbString       expr_to_string            (AstNode *expression);
+void           check_entity_decl         (Checker *c, Entity *e, DeclInfo *decl, Type *named_type, CycleChecker *cycle_checker = NULL);
+void           check_proc_body           (Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body);
+void           update_expr_type          (Checker *c, AstNode *e, Type *type, b32 final);
+
+
+
+b32 check_is_assignable_to_using_subtype(Checker *c, Type *dst, Type *src) {
+	return false;
+}
+
+b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argument = false) {
+	if (operand->mode == Addressing_Invalid ||
+	    type == t_invalid) {
+		return true;
+	}
+
+	Type *s = operand->type;
+
+	if (are_types_identical(s, type))
+		return true;
+
+	Type *src = get_base_type(s);
+	Type *dst = get_base_type(type);
+
+	if (is_type_untyped(src)) {
+		switch (dst->kind) {
+		case Type_Basic:
+			if (operand->mode == Addressing_Constant)
+				return check_value_is_expressible(c, operand->value, dst, NULL);
+			if (src->kind == Type_Basic)
+				return src->Basic.kind == Basic_UntypedBool && is_type_boolean(dst);
+			break;
+		case Type_Pointer:
+			return src->Basic.kind == Basic_UntypedPointer;
+		}
+	}
+
+	if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src)))
+		return true;
+
+	if (is_type_pointer(dst) && is_type_rawptr(src))
+	    return true;
+
+	if (is_type_rawptr(dst) && is_type_pointer(src))
+	    return true;
+
+	if (dst->kind == Type_Array && src->kind == Type_Array) {
+		if (are_types_identical(dst->Array.elem, src->Array.elem)) {
+			return dst->Array.count == src->Array.count;
+		}
+	}
+
+	if (dst->kind == Type_Slice && src->kind == Type_Slice) {
+		if (are_types_identical(dst->Slice.elem, src->Slice.elem)) {
+			return true;
+		}
+	}
+
+	if (is_argument) {
+		// Polymorphism for subtyping
+		if (check_is_assignable_to_using_subtype(c, dst, src)) {
+			return true;
+		}
+	}
+
+
+	return false;
+
+}
+
+
+// NOTE(bill): `content_name` is for debugging
+// TODO(bill): Maybe allow assignment to tuples?
+void check_assignment(Checker *c, Operand *operand, Type *type, String context_name, b32 is_argument = false) {
+	check_not_tuple(c, operand);
+	if (operand->mode == Addressing_Invalid)
+		return;
+
+	if (is_type_untyped(operand->type)) {
+		Type *target_type = type;
+
+		if (type == NULL)
+			target_type = default_type(operand->type);
+		convert_to_typed(c, operand, target_type);
+		if (operand->mode == Addressing_Invalid)
+			return;
+	}
+
+	if (type != NULL) {
+		if (!check_is_assignable_to(c, operand, type, is_argument)) {
+			gbString type_string = type_to_string(type);
+			gbString op_type_string = type_to_string(operand->type);
+			gbString expr_str = expr_to_string(operand->expr);
+			defer (gb_string_free(type_string));
+			defer (gb_string_free(op_type_string));
+			defer (gb_string_free(expr_str));
+
+
+			// TODO(bill): is this a good enough error message?
+			error(&c->error_collector, ast_node_token(operand->expr),
+			      "Cannot assign value `%s` of type `%s` to `%s` in %.*s",
+			      expr_str,
+			      op_type_string,
+			      type_string,
+			      LIT(context_name));
+
+			operand->mode = Addressing_Invalid;
+		}
+	}
+}
+
 
 
 void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *> *entity_map) {
 void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *> *entity_map) {
 	t = get_base_type(type_deref(t));
 	t = get_base_type(type_deref(t));
@@ -1095,7 +1202,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 
 
 	if (!are_types_identical(x->type, y->type)) {
 	if (!are_types_identical(x->type, y->type)) {
 		if (x->type != t_invalid &&
 		if (x->type != t_invalid &&
-		    y->type  != t_invalid) {
+		    y->type != t_invalid) {
 			gbString xt = type_to_string(x->type);
 			gbString xt = type_to_string(x->type);
 			gbString yt = type_to_string(y->type);
 			gbString yt = type_to_string(y->type);
 			defer (gb_string_free(xt));
 			defer (gb_string_free(xt));
@@ -1906,6 +2013,166 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		operand->type = make_type_vector(c->allocator, elem_type, arg_count);
 		operand->type = make_type_vector(c->allocator, elem_type, arg_count);
 		operand->mode = Addressing_Value;
 		operand->mode = Addressing_Value;
 	} break;
 	} break;
+
+	case BuiltinProc_ptr_offset: {
+		// ptr_offset :: proc(ptr: ^T, offset: int) -> ^T
+		// ^T cannot be rawptr
+		Type *ptr_type = get_base_type(operand->type);
+		if (!is_type_pointer(ptr_type)) {
+			gbString type_str = type_to_string(operand->type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Expected a pointer to `ptr_offset`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+		if (ptr_type == t_rawptr) {
+			error(&c->error_collector, ast_node_token(call),
+			      "`rawptr` cannot have pointer arithmetic");
+			return false;
+		}
+
+		AstNode *offset = ce->arg_list->next;
+		Operand op = {};
+		check_expr(c, &op, offset);
+		if (op.mode == Addressing_Invalid)
+			return false;
+		Type *offset_type = get_base_type(op.type);
+		if (!is_type_integer(offset_type)) {
+			error(&c->error_collector, ast_node_token(op.expr), "Pointer offsets for `ptr_offset` must be an integer");
+			return false;
+		}
+
+		if (operand->mode == Addressing_Constant &&
+		    op.mode == Addressing_Constant) {
+			u8 *ptr = cast(u8 *)operand->value.value_pointer;
+			isize elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem);
+			ptr += elem_size * op.value.value_integer;
+			operand->value.value_pointer = ptr;
+		} else {
+			operand->mode = Addressing_Value;
+		}
+
+	} break;
+
+	case BuiltinProc_ptr_sub: {
+		// ptr_sub :: proc(a, b: ^T) -> int
+		// ^T cannot be rawptr
+		Type *ptr_type = get_base_type(operand->type);
+		if (!is_type_pointer(ptr_type)) {
+			gbString type_str = type_to_string(operand->type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Expected a pointer to `ptr_add`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+		if (ptr_type == t_rawptr) {
+			error(&c->error_collector, ast_node_token(call),
+			      "`rawptr` cannot have pointer arithmetic");
+			return false;
+		}
+		AstNode *offset = ce->arg_list->next;
+		Operand op = {};
+		check_expr(c, &op, offset);
+		if (op.mode == Addressing_Invalid)
+			return false;
+		if (!is_type_pointer(op.type)) {
+			gbString type_str = type_to_string(operand->type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Expected a pointer to `ptr_add`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+		if (get_base_type(op.type) == t_rawptr) {
+			error(&c->error_collector, ast_node_token(call),
+			      "`rawptr` cannot have pointer arithmetic");
+			return false;
+		}
+
+		if (!are_types_identical(operand->type, op.type)) {
+			gbString a = type_to_string(operand->type);
+			gbString b = type_to_string(op.type);
+			defer (gb_string_free(a));
+			defer (gb_string_free(b));
+			error(&c->error_collector, ast_node_token(op.expr),
+			      "`ptr_sub` requires to pointer of the same type. Got `%s` and `%s`.", a, b);
+			return false;
+		}
+
+		operand->type = t_int;
+
+		if (operand->mode == Addressing_Constant &&
+		    op.mode == Addressing_Constant) {
+			u8 *ptr_a = cast(u8 *)operand->value.value_pointer;
+			u8 *ptr_b = cast(u8 *)op.value.value_pointer;
+			isize elem_size = type_size_of(c->sizes, c->allocator, ptr_type->Pointer.elem);
+			operand->value = make_exact_value_integer((ptr_a - ptr_b) / elem_size);
+		} else {
+			operand->mode = Addressing_Value;
+		}
+	} break;
+
+	case BuiltinProc_slice_ptr: {
+		// slice_ptr :: proc(a: ^T, len: int[, cap: int]) -> []T
+		// ^T cannot be rawptr
+		Type *ptr_type = get_base_type(operand->type);
+		if (!is_type_pointer(ptr_type)) {
+			gbString type_str = type_to_string(operand->type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Expected a pointer to `ptr_add`, got `%s`",
+			      type_str);
+			return false;
+		}
+
+		if (ptr_type == t_rawptr) {
+			error(&c->error_collector, ast_node_token(call),
+			      "`rawptr` cannot have pointer arithmetic");
+			return false;
+		}
+		AstNode *len = ce->arg_list->next;
+		Operand op = {};
+		check_expr(c, &op, len);
+		if (op.mode == Addressing_Invalid)
+			return false;
+		if (!is_type_integer(op.type)) {
+			gbString type_str = type_to_string(operand->type);
+			defer (gb_string_free(type_str));
+			error(&c->error_collector, ast_node_token(call),
+			      "Length for `slice_ptr` must be an integer, got `%s`",
+			      type_str);
+			return false;
+		}
+
+		AstNode *cap = len->next;
+		if (cap != NULL) {
+			check_expr(c, &op, len);
+			if (op.mode == Addressing_Invalid)
+				return false;
+			if (!is_type_integer(op.type)) {
+				gbString type_str = type_to_string(operand->type);
+				defer (gb_string_free(type_str));
+				error(&c->error_collector, ast_node_token(call),
+				      "Capacity for `slice_ptr` must be an integer, got `%s`",
+				      type_str);
+				return false;
+			}
+			if (cap->next != NULL) {
+				error(&c->error_collector, ast_node_token(call),
+				      "Too many arguments to `slice_ptr`, expected either 2 or 3");
+				return false;
+			}
+		}
+
+		operand->type = make_type_slice(c->allocator, ptr_type->Pointer.elem);
+		operand->mode = Addressing_Value;
+	} break;
+
 	}
 	}
 
 
 	return true;
 	return true;
@@ -1937,7 +2204,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 				continue;
 				continue;
 			if (operand->type->kind != Type_Tuple) {
 			if (operand->type->kind != Type_Tuple) {
 				check_not_tuple(c, operand);
 				check_not_tuple(c, operand);
-				check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"));
+				check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"), true);
 				param_index++;
 				param_index++;
 			} else {
 			} else {
 				auto *tuple = &operand->type->Tuple;
 				auto *tuple = &operand->type->Tuple;
@@ -1949,7 +2216,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 					operand->type = e->type;
 					operand->type = e->type;
 					operand->mode = Addressing_Value;
 					operand->mode = Addressing_Value;
 					check_not_tuple(c, operand);
 					check_not_tuple(c, operand);
-					check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"));
+					check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"), true);
 				}
 				}
 
 
 				if (i < tuple->variable_count && param_index == param_count) {
 				if (i < tuple->variable_count && param_index == param_count) {
@@ -2367,11 +2634,15 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
 			o->mode = Addressing_Variable;
 			o->mode = Addressing_Variable;
 			break;
 			break;
 
 
-		case Type_Pointer:
-			valid = true;
-			o->mode = Addressing_Variable;
-			o->type = get_base_type(t->Pointer.elem);
-			break;
+		case Type_Pointer: {
+			Type *bt = get_base_type(t->Pointer.elem);
+			if (bt->kind == Type_Array) {
+				valid = true;
+				max_count = bt->Array.count;
+				o->mode = Addressing_Variable;
+				o->type = bt->Array.elem;
+			}
+		} break;
 		}
 		}
 
 
 		if (!valid) {
 		if (!valid) {
@@ -2409,7 +2680,6 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
 					max_count = o->value.value_string.len;
 					max_count = o->value.value_string.len;
 				}
 				}
 				o->type = t_string;
 				o->type = t_string;
-				o->mode = Addressing_Value;
 			}
 			}
 			break;
 			break;
 
 
@@ -2423,19 +2693,20 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
 				goto error;
 				goto error;
 			}
 			}
 			o->type = make_type_slice(c->allocator, t->Array.elem);
 			o->type = make_type_slice(c->allocator, t->Array.elem);
-			o->mode = Addressing_Value;
 			break;
 			break;
 
 
 		case Type_Slice:
 		case Type_Slice:
 			valid = true;
 			valid = true;
-			o->mode = Addressing_Value;
 			break;
 			break;
 
 
-		case Type_Pointer:
-			valid = true;
-			o->type = make_type_slice(c->allocator, get_base_type(t->Pointer.elem));
-			o->mode = Addressing_Value;
-			break;
+		case Type_Pointer: {
+			Type *bt = get_base_type(t->Pointer.elem);
+			if (bt->kind == Type_Array) {
+				valid = true;
+				max_count = bt->Array.count;
+				o->type = make_type_slice(c->allocator, bt->Array.elem);
+			}
+		} break;
 		}
 		}
 
 
 		if (!valid) {
 		if (!valid) {
@@ -2445,6 +2716,8 @@ ExpressionKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *typ
 			goto error;
 			goto error;
 		}
 		}
 
 
+		o->mode = Addressing_Value;
+
 		i64 indices[3] = {};
 		i64 indices[3] = {};
 		AstNode *nodes[3] = {se->low, se->high, se->max};
 		AstNode *nodes[3] = {se->low, se->high, se->max};
 		for (isize i = 0; i < gb_count_of(nodes); i++) {
 		for (isize i = 0; i < gb_count_of(nodes); i++) {

+ 0 - 96
src/checker/stmt.cpp

@@ -70,102 +70,6 @@ b32 check_is_terminating(Checker *c, AstNode *node) {
 	return false;
 	return false;
 }
 }
 
 
-
-b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type) {
-	if (operand->mode == Addressing_Invalid ||
-	    type == t_invalid) {
-		return true;
-	}
-
-	Type *s = operand->type;
-
-	if (are_types_identical(s, type))
-		return true;
-
-	Type *sb = get_base_type(s);
-	Type *tb = get_base_type(type);
-
-	if (is_type_untyped(sb)) {
-		switch (tb->kind) {
-		case Type_Basic:
-			if (operand->mode == Addressing_Constant)
-				return check_value_is_expressible(c, operand->value, tb, NULL);
-			if (sb->kind == Type_Basic)
-				return sb->Basic.kind == Basic_UntypedBool && is_type_boolean(tb);
-			break;
-		case Type_Pointer:
-			return sb->Basic.kind == Basic_UntypedPointer;
-		}
-	}
-
-	if (are_types_identical(sb, tb) && (!is_type_named(sb) || !is_type_named(tb)))
-		return true;
-
-	if (is_type_pointer(sb) && is_type_rawptr(tb))
-	    return true;
-
-	if (is_type_rawptr(sb) && is_type_pointer(tb))
-	    return true;
-
-	if (sb->kind == Type_Array && tb->kind == Type_Array) {
-		if (are_types_identical(sb->Array.elem, tb->Array.elem)) {
-			return sb->Array.count == tb->Array.count;
-		}
-	}
-
-	if (sb->kind == Type_Slice && tb->kind == Type_Slice) {
-		if (are_types_identical(sb->Slice.elem, tb->Slice.elem)) {
-			return true;
-		}
-	}
-
-
-	return false;
-
-}
-
-
-// NOTE(bill): `content_name` is for debugging
-// TODO(bill): Maybe allow assignment to tuples?
-void check_assignment(Checker *c, Operand *operand, Type *type, String context_name) {
-	check_not_tuple(c, operand);
-	if (operand->mode == Addressing_Invalid)
-		return;
-
-	if (is_type_untyped(operand->type)) {
-		Type *target_type = type;
-
-		if (type == NULL)
-			target_type = default_type(operand->type);
-		convert_to_typed(c, operand, target_type);
-		if (operand->mode == Addressing_Invalid)
-			return;
-	}
-
-	if (type != NULL) {
-		if (!check_is_assignable_to(c, operand, type)) {
-			gbString type_string = type_to_string(type);
-			gbString op_type_string = type_to_string(operand->type);
-			gbString expr_str = expr_to_string(operand->expr);
-			defer (gb_string_free(type_string));
-			defer (gb_string_free(op_type_string));
-			defer (gb_string_free(expr_str));
-
-
-			// TODO(bill): is this a good enough error message?
-			error(&c->error_collector, ast_node_token(operand->expr),
-			      "Cannot assign value `%s` of type `%s` to `%s` in %.*s",
-			      expr_str,
-			      op_type_string,
-			      type_string,
-			      LIT(context_name));
-
-			operand->mode = Addressing_Invalid;
-		}
-	}
-}
-
-
 Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
 Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
 	if (op_a->mode == Addressing_Invalid ||
 	if (op_a->mode == Addressing_Invalid ||
 	    op_a->type == t_invalid) {
 	    op_a->type == t_invalid) {

+ 71 - 14
src/codegen/ssa.cpp

@@ -985,9 +985,9 @@ ssaValue *ssa_emit_arith(ssaProcedure *proc, Token op, ssaValue *left, ssaValue
 		break;
 		break;
 	}
 	}
 
 
-	ssaValue *v = ssa_make_instr_binary_op(proc, op, left, right);
+	ssaValue *v = ssa_emit(proc, ssa_make_instr_binary_op(proc, op, left, right));
 	ssa_set_type(v, type);
 	ssa_set_type(v, type);
-	return ssa_emit(proc, v);
+	return v;
 }
 }
 
 
 ssaValue *ssa_emit_comp(ssaProcedure *proc, Token op, ssaValue *left, ssaValue *right) {
 ssaValue *ssa_emit_comp(ssaProcedure *proc, Token op, ssaValue *left, ssaValue *right) {
@@ -1696,18 +1696,23 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					// copy :: proc(dst, src: []Type) -> int
 					// copy :: proc(dst, src: []Type) -> int
 					AstNode *dst_node = ce->arg_list;
 					AstNode *dst_node = ce->arg_list;
 					AstNode *src_node = ce->arg_list->next;
 					AstNode *src_node = ce->arg_list->next;
-					ssaValue *dst_slice = ssa_build_addr(proc, dst_node).addr;
-					ssaValue *src_slice = ssa_build_addr(proc, src_node).addr;
+					ssaValue *dst_slice = ssa_build_expr(proc, dst_node);
+					ssaValue *src_slice = ssa_build_expr(proc, src_node);
 					Type *slice_type = get_base_type(ssa_type(dst_slice));
 					Type *slice_type = get_base_type(ssa_type(dst_slice));
 					GB_ASSERT(slice_type->kind == Type_Slice);
 					GB_ASSERT(slice_type->kind == Type_Slice);
 					Type *elem_type = slice_type->Slice.elem;
 					Type *elem_type = slice_type->Slice.elem;
 					i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
 					i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
 
 
-					ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr);
-					ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr);
+					ssaValue *d = ssa_add_local_generated(proc, slice_type);
+					ssaValue *s = ssa_add_local_generated(proc, slice_type);
+					ssa_emit_store(proc, d, dst_slice);
+					ssa_emit_store(proc, s, src_slice);
 
 
-					ssaValue *len_dst = ssa_slice_len(proc, dst_slice);
-					ssaValue *len_src = ssa_slice_len(proc, src_slice);
+					ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, d), t_rawptr);
+					ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, s), t_rawptr);
+
+					ssaValue *len_dst = ssa_slice_len(proc, d);
+					ssaValue *len_src = ssa_slice_len(proc, s);
 
 
 					Token lt = {Token_Lt};
 					Token lt = {Token_Lt};
 					ssaValue *cond = ssa_emit_comp(proc, lt, len_dst, len_src);
 					ssaValue *cond = ssa_emit_comp(proc, lt, len_dst, len_src);
@@ -1795,6 +1800,51 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					return ssa_emit(proc, ssa_make_instr_shuffle_vector(proc, vector, indices, index_count));
 					return ssa_emit(proc, ssa_make_instr_shuffle_vector(proc, vector, indices, index_count));
 
 
 				} break;
 				} break;
+
+				case BuiltinProc_ptr_offset: {
+					ssaValue *ptr = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *offset = ssa_build_expr(proc, ce->arg_list->next);
+					return ssa_emit_ptr_offset(proc, ptr, offset);
+				} break;
+
+				case BuiltinProc_ptr_sub: {
+					ssaValue *ptr_a = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *ptr_b = ssa_build_expr(proc, ce->arg_list->next);
+					Type *ptr_type = get_base_type(ssa_type(ptr_a));
+					GB_ASSERT(ptr_type->kind == Type_Pointer);
+					isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem);
+					Token sub = {Token_Sub};
+					ssaValue *v = ssa_emit_arith(proc, sub, ptr_a, ptr_b, t_int);
+					if (elem_size > 1) {
+						Token quo = {Token_Quo};
+						ssaValue *ez = ssa_make_value_constant(proc->module->allocator, t_int,
+						                                       make_exact_value_integer(elem_size));
+						v = ssa_emit_arith(proc, quo, v, ez, t_int);
+					}
+
+					return v;
+				} break;
+
+				case BuiltinProc_slice_ptr: {
+					ssaValue *ptr = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *len = ssa_build_expr(proc, ce->arg_list->next);
+					ssaValue *cap = len;
+
+					len = ssa_emit_conv(proc, len, t_int);
+
+					if (ce->arg_list->next->next != NULL) {
+						cap = ssa_build_expr(proc, ce->arg_list->next->next);
+						cap = ssa_emit_conv(proc, cap, t_int);
+					}
+
+
+					Type *slice_type = make_type_slice(proc->module->allocator, type_deref(ssa_type(ptr)));
+					ssaValue *slice = ssa_add_local_generated(proc, slice_type);
+					ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_zero32, ssa_type(ptr)), ptr);
+					ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_one32,  t_int),         len);
+					ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_two32,  t_int),         cap);
+					return ssa_emit_load(proc, slice);
+				} break;
 				}
 				}
 			}
 			}
 		}
 		}
@@ -2010,7 +2060,8 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
 			}
 			}
 		} break;
 		} break;
 		case Type_Pointer: {
 		case Type_Pointer: {
-			elem = ssa_emit_load(proc, ssa_build_addr(proc, ie->expr).addr);
+			ssaValue *array = ssa_emit_load(proc, ssa_build_expr(proc, ie->expr));
+			elem = ssa_array_elem(proc, array);
 		} break;
 		} break;
 		}
 		}
 
 
@@ -2050,18 +2101,24 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
 	case_end;
 	case_end;
 
 
 	case_ast_node(de, DerefExpr, expr);
 	case_ast_node(de, DerefExpr, expr);
-		ssaValue *e = ssa_emit_load(proc, ssa_build_addr(proc, de->expr).addr);
-		ssaValue *gep = ssa_make_instr_get_element_ptr(proc, e, NULL, NULL, 0, false);
+		ssaValue *e = ssa_build_expr(proc, de->expr);
+		ssaValue *gep = ssa_emit_zero_gep(proc, e);
+		// HACK(bill): need to deref here as stack variables are of type pointer
+		// and addresses are already pointers
+		// TODO(bill): Completely redo the type system for SSA
 		Type *t = type_deref(ssa_type(e));
 		Type *t = type_deref(ssa_type(e));
 		gep->Instr.GetElementPtr.result_type  = t;
 		gep->Instr.GetElementPtr.result_type  = t;
 		gep->Instr.GetElementPtr.elem_type = t;
 		gep->Instr.GetElementPtr.elem_type = t;
-		ssaValue *v = ssa_emit(proc, gep);
-		return ssa_make_addr(v, expr);
+		return ssa_make_addr(gep, expr);
 	case_end;
 	case_end;
 	}
 	}
 
 
+	TokenPos token_pos = ast_node_token(expr).pos;
 	GB_PANIC("Unexpected address expression\n"
 	GB_PANIC("Unexpected address expression\n"
-	         "\tAstNode: %.*s\n", LIT(ast_node_strings[expr->kind]));
+	         "\tAstNode: %.*s @ "
+	         "%.*s(%td:%td)\n",
+	         LIT(ast_node_strings[expr->kind]),
+	         LIT(token_pos.file), token_pos.line, token_pos.column);
 
 
 
 
 	return ssa_make_addr(NULL, NULL);
 	return ssa_make_addr(NULL, NULL);

+ 2 - 0
src/unicode.cpp

@@ -2,6 +2,8 @@
 #pragma warning(disable: 4245)
 #pragma warning(disable: 4245)
 
 
 #include "utf8proc/utf8proc.h"
 #include "utf8proc/utf8proc.h"
+// #define UTF8PROC_IMPLEMENTATION
+// #include "utf8proc/utf8proc_new.h"
 
 
 #pragma warning(pop)
 #pragma warning(pop)