Browse Source

Variable declaration and assign, unary operators

gingerBill 9 years ago
parent
commit
70f6282f41
7 changed files with 273 additions and 93 deletions
  1. 15 11
      examples/test.ll
  2. 4 9
      examples/test.odin
  3. 1 1
      src/checker/statements.cpp
  4. 6 0
      src/checker/type.cpp
  5. 11 8
      src/codegen/print.cpp
  6. 227 57
      src/codegen/ssa.cpp
  7. 9 7
      src/tokenizer.cpp

+ 15 - 11
examples/test.ll

@@ -1,16 +1,20 @@
 define void @main() {
 define void @main() {
 entry:
 entry:
-	%0 = alloca i64, align 8
+	%0 = alloca i64, align 8 ; a
 	store i64 zeroinitializer, i64* %0
 	store i64 zeroinitializer, i64* %0
-	store i64 137, i64* %0
-	%1 = load i64, i64* %0
-	add i64 1, %1
-	store i64 %2, i64* %0
-	%3 = alloca float, align 4
-	store float zeroinitializer, float* %3
-	store float 0x3f8147ae00000000, float* %3
-	%4 = load float, float* %3
-	fadd float %4, 0x3f7d70a400000000
-	store float %5, float* %3
+	%1 = alloca i64, align 8 ; b
+	store i64 zeroinitializer, i64* %1
+	store i64 1, i64* %0
+	store i64 2, i64* %1
+	%2 = load i64, i64* %0
+	%3 = add i64 %2, 1
+	store i64 %3, i64* %0
+	%4 = load i64, i64* %1
+	%5 = add i64 %4, 1
+	store i64 %5, i64* %1
+	%6 = load i64, i64* %1
+	%7 = load i64, i64* %0
+	%8 = add i64 %7, %6
+	store i64 %8, i64* %0
 	ret void
 	ret void
 }
 }

+ 4 - 9
examples/test.odin

@@ -1,11 +1,6 @@
 main :: proc() {
 main :: proc() {
-	// x : string;
-	// x = "Hello";
-	x : int;
-	x = 137;
-	x = 1 + x;
-
-	y : f32;
-	y = 1.01;
-	y = y + 0.99;
+	a, b := 1, 2;
+	a++;
+	b++;
+	a += b;
 }
 }

+ 1 - 1
src/checker/statements.cpp

@@ -631,7 +631,7 @@ void check_statement(Checker *c, AstNode *node, u32 flags) {
 				error(&c->error_collector, op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
 				error(&c->error_collector, op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
 				return;
 				return;
 			}
 			}
-			if (!gb_is_between(op.kind, Token_AddEq, Token_ModEq)) {
+			if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) {
 				error(&c->error_collector, op, "Unknown Assignment operation `%.*s`", LIT(op.string));
 				error(&c->error_collector, op, "Unknown Assignment operation `%.*s`", LIT(op.string));
 				return;
 				return;
 			}
 			}

+ 6 - 0
src/checker/type.cpp

@@ -208,6 +208,12 @@ Type *make_type_procedure(gbAllocator a, Scope *scope, Type *params, isize param
 }
 }
 
 
 
 
+Type *type_deref(Type *t) {
+	if (t != NULL && t->kind == Type_Pointer)
+		return t->pointer.element;
+	return t;
+}
+
 
 
 #define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1}
 #define STR_LIT(x) {cast(u8 *)(x), gb_size_of(x)-1}
 gb_global Type basic_types[] = {
 gb_global Type basic_types[] = {

+ 11 - 8
src/codegen/print.cpp

@@ -146,7 +146,7 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) {
 			if (i > 0) ssa_fprintf(f, ", ");
 			if (i > 0) ssa_fprintf(f, ", ");
 			ssa_print_type(f, s, &t->procedure.params[i]);
 			ssa_print_type(f, s, &t->procedure.params[i]);
 		}
 		}
-		ssa_fprintf(f, ")");
+		ssa_fprintf(f, ") ");
 		break;
 		break;
 	}
 	}
 }
 }
@@ -159,7 +159,7 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type
 	case ExactValue_String: {
 	case ExactValue_String: {
 		ssa_fprintf(f, "{");
 		ssa_fprintf(f, "{");
 		ssa_print_type(f, m->sizes, &basic_types[Basic_i8]);
 		ssa_print_type(f, m->sizes, &basic_types[Basic_i8]);
-		ssa_fprintf(f, "* \"");
+		ssa_fprintf(f, "* c\"");
 		// TODO(bill): Make unquote string function
 		// TODO(bill): Make unquote string function
 		String unquoted = value.value_string;
 		String unquoted = value.value_string;
 		unquoted.text++;
 		unquoted.text++;
@@ -209,7 +209,7 @@ void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint)
 	}
 	}
 	switch (value->kind) {
 	switch (value->kind) {
 	case ssaValue_TypeName:
 	case ssaValue_TypeName:
-		ssa_print_encoded_local(f, value->type_name->token.string);
+		ssa_print_encoded_local(f, value->type_name.entity->token.string);
 		break;
 		break;
 	case ssaValue_Global:
 	case ssaValue_Global:
 		ssa_print_encoded_global(f, value->global.entity->token.string);
 		ssa_print_encoded_global(f, value->global.entity->token.string);
@@ -237,7 +237,9 @@ void ssa_print_instruction(gbFile *f, ssaModule *m, ssaValue *value) {
 		Type *type = instr->local.entity->type;
 		Type *type = instr->local.entity->type;
 		ssa_fprintf(f, "%%%d = alloca ", value->id);
 		ssa_fprintf(f, "%%%d = alloca ", value->id);
 		ssa_print_type(f, m->sizes, type);
 		ssa_print_type(f, m->sizes, type);
-		ssa_fprintf(f, ", align %lld\n", type_align_of(m->sizes, gb_heap_allocator(), type));
+		ssa_fprintf(f, ", align %lld ", type_align_of(m->sizes, gb_heap_allocator(), type));
+		ssa_fprintf(f, "; %.*s", LIT(instr->local.entity->token.string));
+		ssa_fprintf(f, "\n");
 		ssa_fprintf(f, "\tstore ");
 		ssa_fprintf(f, "\tstore ");
 		ssa_print_type(f, m->sizes, type);
 		ssa_print_type(f, m->sizes, type);
 		ssa_fprintf(f, " zeroinitializer, ");
 		ssa_fprintf(f, " zeroinitializer, ");
@@ -271,9 +273,11 @@ void ssa_print_instruction(gbFile *f, ssaModule *m, ssaValue *value) {
 
 
 
 
 	case ssaInstruction_BinaryOp: {
 	case ssaInstruction_BinaryOp: {
-		ssaBinaryOp *bo = &value->instruction.binary_op;
+		auto *bo = &value->instruction.binary_op;
 		Type *type = ssa_value_type(bo->left);
 		Type *type = ssa_value_type(bo->left);
 
 
+		ssa_fprintf(f, "%%%d = ", value->id);
+
 		if (is_type_float(type))
 		if (is_type_float(type))
 			ssa_fprintf(f, "f");
 			ssa_fprintf(f, "f");
 
 
@@ -320,15 +324,14 @@ void ssa_print_instruction(gbFile *f, ssaModule *m, ssaValue *value) {
 }
 }
 
 
 void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
 void ssa_print_llvm_ir(gbFile *f, ssaModule *m) {
-	gb_printf("-- Printing LLVM-IR -- \n\n");
 	gb_for_array(member_index, m->members.entries) {
 	gb_for_array(member_index, m->members.entries) {
 		auto *entry = &m->members.entries[member_index];
 		auto *entry = &m->members.entries[member_index];
 		ssaValue *v = entry->value;
 		ssaValue *v = entry->value;
 		switch (v->kind) {
 		switch (v->kind) {
 		case ssaValue_TypeName: {
 		case ssaValue_TypeName: {
-			ssa_print_encoded_local(f, v->type_name->token.string);
+			ssa_print_encoded_local(f, v->type_name.entity->token.string);
 			ssa_fprintf(f, " = type ");
 			ssa_fprintf(f, " = type ");
-			ssa_print_type(f, m->sizes, get_base_type(v->type_name->type));
+			ssa_print_type(f, m->sizes, get_base_type(v->type_name.type));
 			ssa_fprintf(f, "\n");
 			ssa_fprintf(f, "\n");
 		} break;
 		} break;
 
 

+ 227 - 57
src/codegen/ssa.cpp

@@ -30,41 +30,35 @@ struct ssaProcedure {
 	ssaModule *module;
 	ssaModule *module;
 	String name;
 	String name;
 	Entity *entity;
 	Entity *entity;
+	Type *type;
 	DeclarationInfo *decl;
 	DeclarationInfo *decl;
 	AstNode *type_expr;
 	AstNode *type_expr;
 	AstNode *body;
 	AstNode *body;
 
 
 	gbArray(ssaValue *) blocks;
 	gbArray(ssaValue *) blocks;
 	ssaBlock *curr_block;
 	ssaBlock *curr_block;
+	gbArray(ssaValue *) anonymous_procedures;
 };
 };
 
 
 
 
-struct ssaLocal {
+
+struct ssaTypeName {
 	Entity *entity;
 	Entity *entity;
+	Type *type;
 };
 };
 struct ssaGlobal {
 struct ssaGlobal {
 	b32 generated;
 	b32 generated;
 	Entity *entity;
 	Entity *entity;
+	Type *type;
 	ssaValue *value;
 	ssaValue *value;
 };
 };
-struct ssaStore {
-	ssaValue *address;
-	ssaValue *value;
-};
-struct ssaLoad {
-	ssaValue *address;
-};
-struct ssaBinaryOp {
-	Token op;
-	ssaValue *left, *right;
-};
-struct ssaGetElementPtr {
-	ssaValue *address;
-	Type *result_type;
-	Type *element_type;
-	gbArray(ssaValue *) indices;
+struct ssaConstant {
+	Type *type;
+	ExactValue value;
 };
 };
 
 
+
+
 enum ssaInstructionKind {
 enum ssaInstructionKind {
 	ssaInstruction_Invalid,
 	ssaInstruction_Invalid,
 
 
@@ -73,6 +67,8 @@ enum ssaInstructionKind {
 	ssaInstruction_Load,
 	ssaInstruction_Load,
 	ssaInstruction_GetElementPtr,
 	ssaInstruction_GetElementPtr,
 
 
+	ssaInstruction_Convert,
+
 	ssaInstruction_BinaryOp,
 	ssaInstruction_BinaryOp,
 
 
 	ssaInstruction_Count,
 	ssaInstruction_Count,
@@ -86,12 +82,31 @@ struct ssaInstruction {
 	TokenPos pos;
 	TokenPos pos;
 
 
 	union {
 	union {
-		ssaLocal         local;
-		ssaStore         store;
-		ssaLoad          load;
-		ssaGetElementPtr get_element_ptr;
+		struct {
+			Entity *entity;
+			Type *type;
+		} local;
+		struct {
+			ssaValue *address;
+			ssaValue *value;
+		} store;
+		struct {
+			ssaValue *address;
+		} load;
+		struct {
+			ssaValue *address;
+			Type *result_type;
+			Type *element_type;
+			isize index_count;
+			isize indices[2];
+		} get_element_ptr;
 
 
-		ssaBinaryOp      binary_op;
+
+
+		struct {
+			Token op;
+			ssaValue *left, *right;
+		} binary_op;
 	};
 	};
 };
 };
 
 
@@ -115,10 +130,10 @@ struct ssaValue {
 	i32 id;
 	i32 id;
 
 
 	union {
 	union {
-		Entity *       type_name;
+		ssaTypeName    type_name;
 		ssaGlobal      global;
 		ssaGlobal      global;
 		ssaProcedure   procedure;
 		ssaProcedure   procedure;
-		TypeAndValue   constant;
+		ssaConstant    constant;
 		ssaBlock       block;
 		ssaBlock       block;
 		ssaInstruction instruction;
 		ssaInstruction instruction;
 	};
 	};
@@ -162,11 +177,12 @@ void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) {
 
 
 
 
 Type *ssa_value_type(ssaValue *value);
 Type *ssa_value_type(ssaValue *value);
+void  ssa_value_set_type(ssaValue *value, Type *type);
 
 
 Type *ssa_instruction_type(ssaInstruction *instr) {
 Type *ssa_instruction_type(ssaInstruction *instr) {
 	switch (instr->kind) {
 	switch (instr->kind) {
 	case ssaInstruction_Local:
 	case ssaInstruction_Local:
-		return instr->local.entity->type;
+		return instr->local.type;
 	case ssaInstruction_Store:
 	case ssaInstruction_Store:
 		return ssa_value_type(instr->store.address);
 		return ssa_value_type(instr->store.address);
 	case ssaInstruction_Load:
 	case ssaInstruction_Load:
@@ -175,14 +191,28 @@ Type *ssa_instruction_type(ssaInstruction *instr) {
 	return NULL;
 	return NULL;
 }
 }
 
 
+void ssa_instruction_set_type(ssaInstruction *instr, Type *type) {
+	switch (instr->kind) {
+	case ssaInstruction_Local:
+		instr->local.type = type;
+		break;
+	case ssaInstruction_Store:
+		ssa_value_set_type(instr->store.value, type);
+		break;
+	case ssaInstruction_Load:
+		// NOTE(bill): Do nothing
+		break;
+	}
+}
+
 Type *ssa_value_type(ssaValue *value) {
 Type *ssa_value_type(ssaValue *value) {
 	switch (value->kind) {
 	switch (value->kind) {
 	case ssaValue_TypeName:
 	case ssaValue_TypeName:
-		return value->type_name->type;
+		return value->type_name.type;
 	case ssaValue_Global:
 	case ssaValue_Global:
-		return value->global.entity->type;
+		return value->global.type;
 	case ssaValue_Procedure:
 	case ssaValue_Procedure:
-		return value->procedure.entity->type;
+		return value->procedure.type;
 	case ssaValue_Constant:
 	case ssaValue_Constant:
 		return value->constant.type;
 		return value->constant.type;
 	case ssaValue_Instruction:
 	case ssaValue_Instruction:
@@ -192,9 +222,32 @@ Type *ssa_value_type(ssaValue *value) {
 }
 }
 
 
 
 
+void ssa_value_set_type(ssaValue *value, Type *type) {
+	switch (value->kind) {
+	case ssaValue_TypeName:
+		value->type_name.type = type;
+		break;
+	case ssaValue_Global:
+		value->global.type = type;
+		break;
+	case ssaValue_Procedure:
+		value->procedure.type = type;
+		break;
+	case ssaValue_Constant:
+		value->constant.type = type;
+		break;
+	case ssaValue_Instruction:
+		ssa_instruction_set_type(&value->instruction, type);
+		break;
+	}
+}
 
 
 
 
 
 
+ssaValue *ssa_build_expression(ssaProcedure *proc, AstNode *expr);
+ssaValue *ssa_build_single_expression(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv);
+ssaLvalue ssa_build_address(ssaProcedure *proc, AstNode *expr);
+ssaValue *ssa_emit_conversion(ssaProcedure *proc, ssaValue *value, Type *a_type);
 
 
 
 
 
 
@@ -215,14 +268,16 @@ ssaValue *ssa_alloc_instruction(gbAllocator a, ssaInstructionKind kind) {
 
 
 ssaValue *ssa_make_value_type_name(gbAllocator a, Entity *e) {
 ssaValue *ssa_make_value_type_name(gbAllocator a, Entity *e) {
 	ssaValue *v = ssa_alloc_value(a, ssaValue_TypeName);
 	ssaValue *v = ssa_alloc_value(a, ssaValue_TypeName);
-	v->type_name = e;
+	v->type_name.entity = e;
+	v->type_name.type = e->type;
 	return v;
 	return v;
 }
 }
 
 
 ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value) {
 ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value) {
 	ssaValue *v = ssa_alloc_value(a, ssaValue_Global);
 	ssaValue *v = ssa_alloc_value(a, ssaValue_Global);
 	v->global.entity = e;
 	v->global.entity = e;
-	v->global.value  = value;
+	v->global.type = e->type;
+	v->global.value = value;
 	return v;
 	return v;
 }
 }
 
 
@@ -232,6 +287,7 @@ ssaValue *ssa_make_instruction_local(ssaProcedure *p, Entity *e) {
 	ssaValue *v = ssa_alloc_instruction(p->module->allocator, ssaInstruction_Local);
 	ssaValue *v = ssa_alloc_instruction(p->module->allocator, ssaInstruction_Local);
 	ssaInstruction *i = &v->instruction;
 	ssaInstruction *i = &v->instruction;
 	i->local.entity = e;
 	i->local.entity = e;
+	i->local.type = e->type;
 	if (p->curr_block) {
 	if (p->curr_block) {
 		gb_array_append(p->curr_block->values, v);
 		gb_array_append(p->curr_block->values, v);
 	}
 	}
@@ -295,6 +351,7 @@ ssaValue *ssa_make_value_procedure(gbAllocator a, Entity *e, DeclarationInfo *de
 	ssaValue *v = ssa_alloc_value(a, ssaValue_Procedure);
 	ssaValue *v = ssa_alloc_value(a, ssaValue_Procedure);
 	v->procedure.module = m;
 	v->procedure.module = m;
 	v->procedure.entity = e;
 	v->procedure.entity = e;
+	v->procedure.type   = e->type;
 	v->procedure.decl   = decl;
 	v->procedure.decl   = decl;
 	v->procedure.name   = e->token.string;
 	v->procedure.name   = e->token.string;
 	return v;
 	return v;
@@ -302,9 +359,9 @@ ssaValue *ssa_make_value_procedure(gbAllocator a, Entity *e, DeclarationInfo *de
 
 
 ssaValue *ssa_make_value_block(gbAllocator a, ssaProcedure *proc, AstNode *node, Scope *scope, String label) {
 ssaValue *ssa_make_value_block(gbAllocator a, ssaProcedure *proc, AstNode *node, Scope *scope, String label) {
 	ssaValue *v = ssa_alloc_value(a, ssaValue_Block);
 	ssaValue *v = ssa_alloc_value(a, ssaValue_Block);
-	v->block.label = label;
-	v->block.node = node;
-	v->block.scope = scope;
+	v->block.label  = label;
+	v->block.node   = node;
+	v->block.scope  = scope;
 	v->block.parent = proc;
 	v->block.parent = proc;
 
 
 	gb_array_init(v->block.instructions, gb_heap_allocator());
 	gb_array_init(v->block.instructions, gb_heap_allocator());
@@ -421,6 +478,8 @@ ssaValue *ssa_emit_store(ssaProcedure *p, ssaValue *address, ssaValue *value) {
 
 
 ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) {
 ssaValue *ssa_emit_load(ssaProcedure *p, ssaValue *address) {
 	ssaValue *v = ssa_make_instruction_load(p, address);
 	ssaValue *v = ssa_make_instruction_load(p, address);
+	Type *t = ssa_value_type(address);
+	ssa_value_set_type(v, type_deref(t));
 	ssa_emit(p, v);
 	ssa_emit(p, v);
 	return v;
 	return v;
 }
 }
@@ -452,22 +511,28 @@ ssaValue *ssa_lvalue_address(ssaLvalue lval, ssaProcedure *p) {
 	return NULL;
 	return NULL;
 }
 }
 
 
-ssaValue *ssa_build_expression(ssaProcedure *proc, AstNode *expr);
-ssaValue *ssa_build_single_expression(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv);
+Type *ssa_lvalue_type(ssaLvalue lval) {
+	switch (lval.kind) {
+	case ssaLvalue_Address:
+		return type_deref(ssa_value_type(lval.address.value));
+	}
+	return NULL;
+}
 
 
-ssaValue *ssa_emit_conversion(ssaProcedure *proc, ssaValue *value, Type *a_type) {
-	Type *b_type = ssa_value_type(value);
-	if (are_types_identical(a_type, b_type))
+ssaValue *ssa_emit_conversion(ssaProcedure *proc, ssaValue *value, Type *t) {
+	Type *src_type = ssa_value_type(value);
+	if (are_types_identical(t, src_type))
 		return value;
 		return value;
 
 
-	Type *a = get_base_type(a_type);
-	Type *b = get_base_type(b_type);
+	Type *dst = get_base_type(t);
+	Type *src = get_base_type(src_type);
 
 
 	if (value->kind == ssaValue_Constant) {
 	if (value->kind == ssaValue_Constant) {
-		if (a->kind == Type_Basic)
-			return ssa_make_value_constant(proc->module->allocator, a_type, value->constant.value);
+		if (dst->kind == Type_Basic)
+			return ssa_make_value_constant(proc->module->allocator, t, value->constant.value);
 	}
 	}
 
 
+
 	GB_PANIC("TODO(bill): ssa_emit_conversion");
 	GB_PANIC("TODO(bill): ssa_emit_conversion");
 
 
 	return NULL;
 	return NULL;
@@ -508,19 +573,36 @@ ssaValue *ssa_build_single_expression(ssaProcedure *proc, AstNode *expr, TypeAnd
 		}
 		}
 	} break;
 	} break;
 
 
+	case AstNode_DereferenceExpression: {
+		ssaLvalue addr = ssa_build_address(proc, expr->dereference_expression.operand);
+		return ssa_lvalue_load(addr, proc);
+	} break;
+
 	case AstNode_UnaryExpression: {
 	case AstNode_UnaryExpression: {
 		auto *ue = &expr->unary_expression;
 		auto *ue = &expr->unary_expression;
 		switch (ue->op.kind) {
 		switch (ue->op.kind) {
+		case Token_Pointer:
+			return ssa_lvalue_address(ssa_build_address(proc, ue->operand), proc);
 		case Token_Add:
 		case Token_Add:
 			return ssa_build_expression(proc, ue->operand);
 			return ssa_build_expression(proc, ue->operand);
-		case Token_Sub:
-			return NULL;
-		case Token_Xor:
-			return NULL;
-		case Token_Not:
-			return NULL;
-		case Token_Pointer:
+		case Token_Sub: {
+			// NOTE(bill): -x == 0 - x
+			ExactValue zero = make_exact_value_integer(0);
+			ssaValue *left = ssa_make_value_constant(proc->module->allocator, tv->type, zero);
+			ssaValue *right = ssa_build_expression(proc, ue->operand);
+			return ssa_emit_arith(proc, ue->op, left, right, tv->type);
+		} break;
+		case Token_Xor: { // Bitwise not
+			// NOTE(bill): "not" x == x "xor" -1
+			ExactValue neg_one = make_exact_value_integer(-1);
+			ssaValue *left = ssa_build_expression(proc, ue->operand);
+			ssaValue *right = ssa_make_value_constant(proc->module->allocator, tv->type, neg_one);
+			return ssa_emit_arith(proc, ue->op, left, right, tv->type);
+		} break;
+		case Token_Not: // Boolean not
+			GB_PANIC("Token_Not");
 			return NULL;
 			return NULL;
+
 		}
 		}
 	} break;
 	} break;
 
 
@@ -550,8 +632,9 @@ ssaValue *ssa_build_single_expression(ssaProcedure *proc, AstNode *expr, TypeAnd
 		break;
 		break;
 	case AstNode_SliceExpression:
 	case AstNode_SliceExpression:
 		break;
 		break;
-	case AstNode_IndexExpression:
-		break;
+	case AstNode_IndexExpression: {
+
+	} break;
 	case AstNode_SelectorExpression:
 	case AstNode_SelectorExpression:
 		break;
 		break;
 	}
 	}
@@ -609,7 +692,8 @@ ssaLvalue ssa_build_address(ssaProcedure *proc, AstNode *expr) {
 
 
 	case AstNode_DereferenceExpression: {
 	case AstNode_DereferenceExpression: {
 		ssaLvalue val = {ssaLvalue_Address};
 		ssaLvalue val = {ssaLvalue_Address};
-		val.address.value = ssa_build_expression(proc, expr);
+		AstNode *operand = expr->dereference_expression.operand;
+		val.address.value = ssa_build_expression(proc, operand);
 		val.address.expr  = expr;
 		val.address.expr  = expr;
 		return val;
 		return val;
 	} break;
 	} break;
@@ -623,10 +707,20 @@ ssaLvalue ssa_build_address(ssaProcedure *proc, AstNode *expr) {
 	// TODO(bill): Others address
 	// TODO(bill): Others address
 	}
 	}
 
 
+	GB_PANIC("Unexpected address expression");
+
 	ssaLvalue blank = {ssaLvalue_Blank};
 	ssaLvalue blank = {ssaLvalue_Blank};
 	return blank;
 	return blank;
 }
 }
 
 
+void ssa_build_assign_op(ssaProcedure *proc, ssaLvalue lhs, ssaValue *value, Token op) {
+	ssaValue *old_value = ssa_lvalue_load(lhs, proc);
+	ssaValue *change = ssa_emit_conversion(proc, value, ssa_value_type(old_value));
+	ssaValue *new_value = ssa_emit_arith(proc, op, old_value, change, ssa_lvalue_type(lhs));
+	ssa_lvalue_store(lhs, proc, new_value);
+}
+
+
 void ssa_build_statement(ssaProcedure *proc, AstNode *s);
 void ssa_build_statement(ssaProcedure *proc, AstNode *s);
 
 
 void ssa_build_statement_list(ssaProcedure *proc, AstNode *list) {
 void ssa_build_statement_list(ssaProcedure *proc, AstNode *list) {
@@ -642,6 +736,32 @@ void ssa_build_statement(ssaProcedure *proc, AstNode *s) {
 		auto *vd = &s->variable_declaration;
 		auto *vd = &s->variable_declaration;
 		if (vd->kind == Declaration_Mutable) {
 		if (vd->kind == Declaration_Mutable) {
 			if (vd->name_count == vd->value_count) { // 1:1 assigment
 			if (vd->name_count == vd->value_count) { // 1:1 assigment
+				gbArray(ssaLvalue)  lvals;
+				gbArray(ssaValue *) inits;
+				gb_array_init_reserve(lvals, gb_heap_allocator(), vd->name_count);
+				gb_array_init_reserve(inits, gb_heap_allocator(), vd->name_count);
+				defer (gb_array_free(lvals));
+				defer (gb_array_free(inits));
+
+				for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+					ssaLvalue lval = {ssaLvalue_Blank};
+					if (!ssa_is_blank_identifier(name)) {
+						ssa_add_local_for_identifier(proc, name);
+						lval = ssa_build_address(proc, name);
+					}
+
+					gb_array_append(lvals, lval);
+				}
+
+				for (AstNode *value = vd->value_list; value != NULL; value = value->next) {
+					ssaValue *init = ssa_build_expression(proc, value);
+					gb_array_append(inits, init);
+				}
+
+
+				gb_for_array(i, inits) {
+					ssa_lvalue_store(lvals[i], proc, inits[i]);
+				}
 
 
 			} else if (vd->value_count == 0) { // declared and zero-initialized
 			} else if (vd->value_count == 0) { // declared and zero-initialized
 				for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
 				for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
@@ -656,6 +776,20 @@ void ssa_build_statement(ssaProcedure *proc, AstNode *s) {
 		}
 		}
 	} break;
 	} break;
 
 
+	case AstNode_IncDecStatement: {
+		Token op = s->inc_dec_statement.op;
+		if (op.kind == Token_Increment) {
+			op.kind = Token_Add;
+		} else if (op.kind == Token_Decrement) {
+			op.kind = Token_Sub;
+		}
+		ssaLvalue lval = ssa_build_address(proc, s->inc_dec_statement.expression);
+		ssaValue *one = ssa_make_value_constant(proc->module->allocator, ssa_lvalue_type(lval),
+		                                        make_exact_value_integer(1));
+		ssa_build_assign_op(proc, lval, one, op);
+
+	} break;
+
 	case AstNode_AssignStatement: {
 	case AstNode_AssignStatement: {
 		auto *assign = &s->assign_statement;
 		auto *assign = &s->assign_statement;
 		switch (assign->op.kind) {
 		switch (assign->op.kind) {
@@ -681,16 +815,36 @@ void ssa_build_statement(ssaProcedure *proc, AstNode *s) {
 					ssaValue *init = ssa_build_expression(proc, rhs);
 					ssaValue *init = ssa_build_expression(proc, rhs);
 					ssa_lvalue_store(lvals[0], proc, init);
 					ssa_lvalue_store(lvals[0], proc, init);
 				} else {
 				} else {
-					GB_PANIC("TODO(bill): parallel assignment");
+					gbArray(ssaValue *) inits;
+					gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(lvals));
+					defer (gb_array_free(inits));
+
+					for (AstNode *rhs = assign->rhs_list; rhs != NULL; rhs = rhs->next) {
+						ssaValue *init = ssa_build_expression(proc, rhs);
+						gb_array_append(inits, init);
+					}
+
+					gb_for_array(i, inits) {
+						ssa_lvalue_store(lvals[i], proc, inits[i]);
+					}
 				}
 				}
 			} else {
 			} else {
-					GB_PANIC("TODO(bill): tuple assignment");
+				GB_PANIC("TODO(bill): tuple assignment");
 			}
 			}
 
 
 		} break;
 		} break;
 
 
-		default: // +=, -=, etc
-			break;
+		default: {
+			// NOTE(bill): Only 1 += 1 is allowed, no tuples
+			// +=, -=, etc
+			Token op = assign->op;
+			i32 kind = op.kind;
+			kind += Token_Add - Token_AddEq; // Convert += to +
+			op.kind = cast(TokenKind)kind;
+			ssaLvalue lhs = ssa_build_address(proc, assign->lhs_list);
+			ssaValue *value = ssa_build_expression(proc, assign->rhs_list);
+			ssa_build_assign_op(proc, lhs, value, op);
+		} break;
 		}
 		}
 	} break;
 	} break;
 
 
@@ -701,6 +855,22 @@ void ssa_build_statement(ssaProcedure *proc, AstNode *s) {
 	case AstNode_BlockStatement:
 	case AstNode_BlockStatement:
 		ssa_build_statement_list(proc, s->block_statement.list);
 		ssa_build_statement_list(proc, s->block_statement.list);
 		break;
 		break;
+
+	case AstNode_IfStatement:
+		GB_PANIC("AstNode_IfStatement");
+		break;
+	case AstNode_ReturnStatement:
+		GB_PANIC("AstNode_ReturnStatement");
+		break;
+	case AstNode_ForStatement:
+		GB_PANIC("AstNode_ForStatement");
+		break;
+	case AstNode_DeferStatement:
+		GB_PANIC("AstNode_DeferStatement");
+		break;
+	case AstNode_BranchStatement:
+		GB_PANIC("AstNode_BranchStatement");
+		break;
 	}
 	}
 }
 }
 
 
@@ -709,7 +879,7 @@ void ssa_build_statement(ssaProcedure *proc, AstNode *s) {
 void ssa_build_procedure(ssaValue *value) {
 void ssa_build_procedure(ssaValue *value) {
 	ssaProcedure *proc = &value->procedure;
 	ssaProcedure *proc = &value->procedure;
 
 
-	gb_printf("Building %.*s: %.*s\n", LIT(entity_strings[proc->entity->kind]), LIT(proc->name));
+	// gb_printf("Building %.*s: %.*s\n", LIT(entity_strings[proc->entity->kind]), LIT(proc->name));
 
 
 
 
 	AstNode *proc_decl = proc->decl->proc_decl;
 	AstNode *proc_decl = proc->decl->proc_decl;

+ 9 - 7
src/tokenizer.cpp

@@ -48,25 +48,29 @@ TOKEN_KIND(_OperatorBegin, "_OperatorBegin"), \
 	TOKEN_KIND(Mul, "*"), \
 	TOKEN_KIND(Mul, "*"), \
 	TOKEN_KIND(Quo, "/"), \
 	TOKEN_KIND(Quo, "/"), \
 	TOKEN_KIND(Mod, "%"), \
 	TOKEN_KIND(Mod, "%"), \
+	TOKEN_KIND(And, "&"), \
+	TOKEN_KIND(Or, "|"), \
+	TOKEN_KIND(Xor, "~"), \
+	TOKEN_KIND(AndNot, "&~"), \
+TOKEN_KIND(_AssignOpBegin, "_AssignOpBegin"), \
 	TOKEN_KIND(AddEq, "+="), \
 	TOKEN_KIND(AddEq, "+="), \
 	TOKEN_KIND(SubEq, "-="), \
 	TOKEN_KIND(SubEq, "-="), \
 	TOKEN_KIND(MulEq, "*="), \
 	TOKEN_KIND(MulEq, "*="), \
 	TOKEN_KIND(QuoEq, "/="), \
 	TOKEN_KIND(QuoEq, "/="), \
 	TOKEN_KIND(ModEq, "%="), \
 	TOKEN_KIND(ModEq, "%="), \
-	TOKEN_KIND(And, "&"), \
-	TOKEN_KIND(Or, "|"), \
-	TOKEN_KIND(Xor, "~"), \
-	TOKEN_KIND(AndNot, "&~"), \
 	TOKEN_KIND(AndEq, "&="), \
 	TOKEN_KIND(AndEq, "&="), \
 	TOKEN_KIND(OrEq, "|="), \
 	TOKEN_KIND(OrEq, "|="), \
 	TOKEN_KIND(XorEq, "~="), \
 	TOKEN_KIND(XorEq, "~="), \
-	TOKEN_KIND(AndNotEq, "&~"), \
+	TOKEN_KIND(AndNotEq, "&~="), \
+TOKEN_KIND(_AssignOpEnd, "_AssignOpEnd"), \
 	TOKEN_KIND(Increment, "++"), \
 	TOKEN_KIND(Increment, "++"), \
 	TOKEN_KIND(Decrement, "--"), \
 	TOKEN_KIND(Decrement, "--"), \
 	TOKEN_KIND(ArrowRight, "->"), \
 	TOKEN_KIND(ArrowRight, "->"), \
 	TOKEN_KIND(ArrowLeft, "<-"), \
 	TOKEN_KIND(ArrowLeft, "<-"), \
 	TOKEN_KIND(CmpAnd, "&&"), \
 	TOKEN_KIND(CmpAnd, "&&"), \
 	TOKEN_KIND(CmpOr, "||"), \
 	TOKEN_KIND(CmpOr, "||"), \
+	TOKEN_KIND(CmpAndEq, "&&="), \
+	TOKEN_KIND(CmpOrEq, "||="), \
 \
 \
 TOKEN_KIND(_ComparisonBegin, "_ComparisonBegin"), \
 TOKEN_KIND(_ComparisonBegin, "_ComparisonBegin"), \
 	TOKEN_KIND(CmpEq, "=="), \
 	TOKEN_KIND(CmpEq, "=="), \
@@ -77,8 +81,6 @@ TOKEN_KIND(_ComparisonBegin, "_ComparisonBegin"), \
 	TOKEN_KIND(GtEq, ">="), \
 	TOKEN_KIND(GtEq, ">="), \
 TOKEN_KIND(_ComparisonEnd, "_ComparisonEnd"), \
 TOKEN_KIND(_ComparisonEnd, "_ComparisonEnd"), \
 \
 \
-	TOKEN_KIND(CmpAndEq, "&&="), \
-	TOKEN_KIND(CmpOrEq, "||="), \
 	TOKEN_KIND(OpenParen, "("), \
 	TOKEN_KIND(OpenParen, "("), \
 	TOKEN_KIND(CloseParen, ")"), \
 	TOKEN_KIND(CloseParen, ")"), \
 	TOKEN_KIND(OpenBracket, "["), \
 	TOKEN_KIND(OpenBracket, "["), \