Browse Source

Fix issue #51; begin work on `atomic` types

Ginger Bill 8 years ago
parent
commit
99125dc743
12 changed files with 232 additions and 135 deletions
  1. 7 13
      code/demo.odin
  2. 1 0
      core/_preload.odin
  3. 0 100
      core/atomic.odin
  4. 7 0
      core/fmt.odin
  5. 12 0
      src/check_expr.c
  6. 19 13
      src/checker.c
  7. 88 0
      src/common.c
  8. 15 6
      src/ir.c
  9. 18 0
      src/ir_print.c
  10. 21 0
      src/parser.c
  11. 1 0
      src/tokenizer.c
  12. 43 3
      src/types.c

+ 7 - 13
code/demo.odin

@@ -1,18 +1,12 @@
 #import "fmt.odin";
 
 main :: proc() {
-	immutable program := "+ + * - /";
-	accumulator := 0;
-
-	for token in program {
-		match token {
-		case '+': accumulator += 1;
-		case '-': accumulator -= 1;
-		case '*': accumulator *= 2;
-		case '/': accumulator /= 2;
-		default: // Ignore everything else
-		}
+	x: atomic int = 123;
+	fmt.println(x);
+	arr :[dynamic]any;
+	append(arr, "123", 123, 3.14159265359878, true);
+	for a in arr {
+		fmt.println(a);
 	}
-
-	fmt.printf("The program \"%s\" calculates the value %d\n", program, accumulator);
+	fmt.print(arr, "\n");
 }

+ 1 - 0
core/_preload.odin

@@ -50,6 +50,7 @@ Type_Info :: union {
 	Pointer{
 		elem: ^Type_Info, // nil -> rawptr
 	},
+	Atomic{elem: ^Type_Info},
 	Procedure{
 		params:     ^Type_Info, // Type_Info.Tuple
 		results:    ^Type_Info, // Type_Info.Tuple

+ 0 - 100
core/atomic.odin

@@ -1,100 +0,0 @@
-// TODO(bill): Use assembly instead here to implement atomics
-// Inline vs external file?
-
-#import win32 "sys/windows.odin" when ODIN_OS == "windows";
-_ := compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
-
-
-yield_thread :: proc() { win32.mm_pause(); }
-mfence       :: proc() { win32.ReadWriteBarrier(); }
-sfence       :: proc() { win32.WriteBarrier(); }
-lfence       :: proc() { win32.ReadBarrier(); }
-
-
-load :: proc(a: ^i32) -> i32 {
-	return a^;
-}
-store :: proc(a: ^i32, value: i32) {
-	a^ = value;
-}
-compare_exchange :: proc(a: ^i32, expected, desired: i32) -> i32 {
-	return win32.InterlockedCompareExchange(a, desired, expected);
-}
-exchanged :: proc(a: ^i32, desired: i32) -> i32 {
-	return win32.InterlockedExchange(a, desired);
-}
-fetch_add :: proc(a: ^i32, operand: i32) -> i32 {
-	return win32.InterlockedExchangeAdd(a, operand);
-
-}
-fetch_and :: proc(a: ^i32, operand: i32) -> i32 {
-	return win32.InterlockedAnd(a, operand);
-}
-fetch_or :: proc(a: ^i32, operand: i32) -> i32 {
-	return win32.InterlockedOr(a, operand);
-}
-spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
-	old_value := compare_exchange(a, 1, 0);
-	counter := 0;
-	for old_value != 0 && (time_out < 0 || counter < time_out) {
-		counter++;
-		yield_thread();
-		old_value = compare_exchange(a, 1, 0);
-		mfence();
-	}
-	return old_value == 0;
-}
-spin_unlock :: proc(a: ^i32) {
-	store(a, 0);
-	mfence();
-}
-try_acquire_lock :: proc(a: ^i32) -> bool {
-	yield_thread();
-	old_value := compare_exchange(a, 1, 0);
-	mfence();
-	return old_value == 0;
-}
-
-
-load :: proc(a: ^i64) -> i64 {
-	return a^;
-}
-store :: proc(a: ^i64, value: i64) {
-	a^ = value;
-}
-compare_exchange :: proc(a: ^i64, expected, desired: i64) -> i64 {
-	return win32.InterlockedCompareExchange64(a, desired, expected);
-}
-exchanged :: proc(a: ^i64, desired: i64) -> i64 {
-	return win32.InterlockedExchange64(a, desired);
-}
-fetch_add :: proc(a: ^i64, operand: i64) -> i64 {
-	return win32.InterlockedExchangeAdd64(a, operand);
-}
-fetch_and :: proc(a: ^i64, operand: i64) -> i64 {
-	return win32.InterlockedAnd64(a, operand);
-}
-fetch_or :: proc(a: ^i64, operand: i64) -> i64 {
-	return win32.InterlockedOr64(a, operand);
-}
-spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
-	old_value := compare_exchange(a, 1, 0);
-	counter := 0;
-	for old_value != 0 && (time_out < 0 || counter < time_out) {
-		counter++;
-		yield_thread();
-		old_value = compare_exchange(a, 1, 0);
-		mfence();
-	}
-	return old_value == 0;
-}
-spin_unlock :: proc(a: ^i64) {
-	store(a, 0);
-	mfence();
-}
-try_acquire_lock :: proc(a: ^i64) -> bool {
-	yield_thread();
-	old_value := compare_exchange(a, 1, 0);
-	mfence();
-	return old_value == 0;
-}

+ 7 - 0
core/fmt.odin

@@ -212,6 +212,10 @@ write_type :: proc(buf: ^String_Buffer, ti: ^Type_Info) {
 		}
 	case String:  write_string(buf, "string");
 	case Boolean: write_string(buf, "bool");
+	case Atomic:
+		write_string(buf, "atomic ");
+		write_type(buf, info.elem);
+
 	case Pointer:
 		if info.elem == nil {
 			write_string(buf, "rawptr");
@@ -813,6 +817,9 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
 			fmt_pointer(fi, (cast(^rawptr)v.data)^, verb);
 		}
 
+	case Atomic:
+		fmt_arg(fi, any{v.data, info.elem}, verb);
+
 	case Array:
 		if verb != 'v' {
 			fmt_bad_verb(fi, verb);

+ 12 - 0
src/check_expr.c

@@ -1539,6 +1539,13 @@ bool check_type_extra_internal(Checker *c, AstNode *e, Type **type, Type *named_
 		return true;
 	case_end;
 
+	case_ast_node(at, AtomicType, e);
+		Type *elem = check_type(c, at->type);
+		i64 esz = type_size_of(c->allocator, elem);
+		*type = make_type_atomic(c->allocator, elem);
+		return true;
+	case_end;
+
 	case_ast_node(at, ArrayType, e);
 		if (at->count != NULL) {
 			Type *elem = check_type_extra(c, at->elem, NULL);
@@ -6214,6 +6221,11 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		str = gb_string_appendc(str, "#type ");
 		str = write_expr_to_string(str, ht->type);
 	case_end;
+
+	case_ast_node(at, AtomicType, node);
+		str = gb_string_appendc(str, "atomic ");
+		str = write_expr_to_string(str, at->type);
+	case_end;
 	}
 
 	return str;

+ 19 - 13
src/checker.c

@@ -994,6 +994,10 @@ void add_type_info_type(Checker *c, Type *t) {
 		add_type_info_type(c, bt->Pointer.elem);
 		break;
 
+	case Type_Atomic:
+		add_type_info_type(c, bt->Atomic.elem);
+		break;
+
 	case Type_Array:
 		add_type_info_type(c, bt->Array.elem);
 		add_type_info_type(c, make_type_pointer(c->allocator, bt->Array.elem));
@@ -1175,7 +1179,7 @@ void init_preload(Checker *c) {
 
 
 
-		if (record->variant_count != 21) {
+		if (record->variant_count != 22) {
 			compiler_error("Invalid `Type_Info` layout");
 		}
 		t_type_info_named         = record->variants[ 1]->type;
@@ -1187,27 +1191,29 @@ void init_preload(Checker *c) {
 		t_type_info_boolean       = record->variants[ 7]->type;
 		t_type_info_any           = record->variants[ 8]->type;
 		t_type_info_pointer       = record->variants[ 9]->type;
-		t_type_info_procedure     = record->variants[10]->type;
-		t_type_info_array         = record->variants[11]->type;
-		t_type_info_dynamic_array = record->variants[12]->type;
-		t_type_info_slice         = record->variants[13]->type;
-		t_type_info_vector        = record->variants[14]->type;
-		t_type_info_tuple         = record->variants[15]->type;
-		t_type_info_struct        = record->variants[16]->type;
-		t_type_info_raw_union     = record->variants[17]->type;
-		t_type_info_union         = record->variants[18]->type;
-		t_type_info_enum          = record->variants[19]->type;
-		t_type_info_map           = record->variants[20]->type;
+		t_type_info_atomic        = record->variants[10]->type;
+		t_type_info_procedure     = record->variants[11]->type;
+		t_type_info_array         = record->variants[12]->type;
+		t_type_info_dynamic_array = record->variants[13]->type;
+		t_type_info_slice         = record->variants[14]->type;
+		t_type_info_vector        = record->variants[15]->type;
+		t_type_info_tuple         = record->variants[16]->type;
+		t_type_info_struct        = record->variants[17]->type;
+		t_type_info_raw_union     = record->variants[18]->type;
+		t_type_info_union         = record->variants[19]->type;
+		t_type_info_enum          = record->variants[20]->type;
+		t_type_info_map           = record->variants[21]->type;
 
 		t_type_info_named_ptr         = make_type_pointer(c->allocator, t_type_info_named);
 		t_type_info_integer_ptr       = make_type_pointer(c->allocator, t_type_info_integer);
 		t_type_info_float_ptr         = make_type_pointer(c->allocator, t_type_info_float);
 		t_type_info_complex_ptr       = make_type_pointer(c->allocator, t_type_info_complex);
-		t_type_info_quaternion_ptr   = make_type_pointer(c->allocator, t_type_info_quaternion);
+		t_type_info_quaternion_ptr    = make_type_pointer(c->allocator, t_type_info_quaternion);
 		t_type_info_string_ptr        = make_type_pointer(c->allocator, t_type_info_string);
 		t_type_info_boolean_ptr       = make_type_pointer(c->allocator, t_type_info_boolean);
 		t_type_info_any_ptr           = make_type_pointer(c->allocator, t_type_info_any);
 		t_type_info_pointer_ptr       = make_type_pointer(c->allocator, t_type_info_pointer);
+		t_type_info_atomic_ptr        = make_type_pointer(c->allocator, t_type_info_atomic);
 		t_type_info_procedure_ptr     = make_type_pointer(c->allocator, t_type_info_procedure);
 		t_type_info_array_ptr         = make_type_pointer(c->allocator, t_type_info_array);
 		t_type_info_dynamic_array_ptr = make_type_pointer(c->allocator, t_type_info_dynamic_array);

+ 88 - 0
src/common.c

@@ -31,6 +31,94 @@ gbAllocator scratch_allocator(void) {
 	return gb_scratch_allocator(&scratch_memory);
 }
 
+typedef struct DynamicArenaBlock DynamicArenaBlock;
+typedef struct DynamicArena      DynamicArena;
+
+struct DynamicArenaBlock {
+	DynamicArenaBlock *prev;
+	DynamicArenaBlock *next;
+	u8 *               start;
+	isize              count;
+	isize              capacity;
+
+	gbVirtualMemory    vm;
+};
+
+struct DynamicArena {
+	DynamicArenaBlock *start_block;
+	DynamicArenaBlock *current_block;
+	isize              block_size;
+};
+
+DynamicArenaBlock *add_dynamic_arena_block(DynamicArena *a) {
+	GB_ASSERT(a != NULL);
+	GB_ASSERT(a->block_size > 0);
+
+	gbVirtualMemory vm = gb_vm_alloc(NULL, a->block_size);
+	DynamicArenaBlock *block = cast(DynamicArenaBlock *)vm.data;
+
+	u8 *start = cast(u8 *)gb_align_forward(cast(u8 *)(block + 1), GB_DEFAULT_MEMORY_ALIGNMENT);
+	u8 *end = cast(u8 *)vm.data + vm.size;
+
+	block->vm       = vm;
+	block->start    = start;
+	block->count    = 0;
+	block->capacity = end-start;
+
+	if (a->current_block != NULL) {
+		a->current_block->next = block;
+		block->prev = a->current_block;
+	}
+	a->current_block = block;
+	return block;
+}
+
+void init_dynamic_arena(DynamicArena *a, isize block_size) {
+	isize size = gb_size_of(DynamicArenaBlock) + block_size;
+	size = cast(isize)gb_align_forward(cast(void *)cast(uintptr)size, GB_DEFAULT_MEMORY_ALIGNMENT);
+	a->block_size = size;
+	a->start_block = add_dynamic_arena_block(a);
+}
+
+void destroy_dynamic_arena(DynamicArena *a) {
+	DynamicArenaBlock *b = a->current_block;
+	while (b != NULL) {
+		gbVirtualMemory vm = b->vm;
+		b = b->prev;
+		gb_vm_free(b->vm);
+	}
+}
+
+GB_ALLOCATOR_PROC(dynamic_arena_allocator_proc) {
+	DynamicArena *a = cast(DynamicArena *)allocator_data;
+	void *ptr = NULL;
+
+	switch (type) {
+	case gbAllocation_Alloc: {
+
+	} break;
+
+	case gbAllocation_Free: {
+	} break;
+
+	case gbAllocation_Resize: {
+	} break;
+
+	case gbAllocation_FreeAll:
+		GB_PANIC("free_all is not supported by this allocator");
+		break;
+	}
+
+	return ptr;
+}
+
+gbAllocator dynamic_arena_allocator(DynamicArena *a) {
+	gbAllocator allocator = {dynamic_arena_allocator_proc, a};
+	return allocator;
+}
+
+
+
 
 i64 next_pow2(i64 n) {
 	if (n <= 0) {

+ 15 - 6
src/ir.c

@@ -156,7 +156,9 @@ struct irProcedure {
 		i64          alignment;                                       \
 	})                                                                \
 	IR_INSTR_KIND(ZeroInit, struct { irValue *address; })             \
-	IR_INSTR_KIND(Store,    struct { irValue *address, *value; })     \
+	IR_INSTR_KIND(Store,    struct {                                  \
+		irValue *address, *value; bool atomic;                        \
+	})                                                                \
 	IR_INSTR_KIND(Load,     struct { Type *type; irValue *address; }) \
 	IR_INSTR_KIND(PtrOffset, struct {                                 \
 		irValue *address;                                             \
@@ -822,11 +824,12 @@ irValue *ir_instr_local(irProcedure *p, Entity *e, bool zero_initialized) {
 }
 
 
-irValue *ir_instr_store(irProcedure *p, irValue *address, irValue *value) {
+irValue *ir_instr_store(irProcedure *p, irValue *address, irValue *value, bool atomic) {
 	irValue *v = ir_alloc_instr(p, irInstr_Store);
 	irInstr *i = &v->Instr;
 	i->Store.address = address;
 	i->Store.value = value;
+	i->Store.atomic = atomic;
 	return v;
 }
 
@@ -1429,7 +1432,7 @@ irValue *ir_emit_store(irProcedure *p, irValue *address, irValue *value) {
 		GB_ASSERT_MSG(are_types_identical(core_type(a), core_type(b)), "%s %s", type_to_string(a), type_to_string(b));
 	}
 #endif
-	return ir_emit(p, ir_instr_store(p, address, value));
+	return ir_emit(p, ir_instr_store(p, address, value, is_type_atomic(a)));
 }
 irValue *ir_emit_load(irProcedure *p, irValue *address) {
 	GB_ASSERT(address != NULL);
@@ -2023,7 +2026,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue *
 		if (is_type_pointer(t_left) && is_type_integer(t_right)) {
 			// ptr - int
 			irValue *ptr = ir_emit_conv(proc, left, type);
-			irValue *offset = right;
+			irValue *offset = ir_emit_unary_arith(proc, Token_Sub, right, t_int);
 			return ir_emit_ptr_offset(proc, ptr, offset);
 		} else if (is_type_pointer(t_left) && is_type_pointer(t_right)) {
 			GB_ASSERT(is_type_integer(type));
@@ -6465,7 +6468,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 		irValue *prev_context = ir_add_local_generated(proc, t_context);
 		ir_emit_store(proc, prev_context, ir_emit_load(proc, context_ptr));
 
-		ir_add_defer_instr(proc, proc->scope_index, ir_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context)));
+		ir_add_defer_instr(proc, proc->scope_index, ir_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context), false));
 
 		irValue *gep = ir_emit_struct_ep(proc, context_ptr, 1);
 		ir_emit_store(proc, gep, ir_build_expr(proc, pa->expr));
@@ -6484,7 +6487,7 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 		irValue *prev_context = ir_add_local_generated(proc, t_context);
 		ir_emit_store(proc, prev_context, ir_emit_load(proc, context_ptr));
 
-		ir_add_defer_instr(proc, proc->scope_index, ir_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context)));
+		ir_add_defer_instr(proc, proc->scope_index, ir_instr_store(proc, context_ptr, ir_emit_load(proc, prev_context), false));
 
 		ir_emit_store(proc, context_ptr, ir_build_expr(proc, pc->expr));
 
@@ -7366,6 +7369,12 @@ void ir_gen_tree(irGen *s) {
 					irValue *gep = ir_get_type_info_ptr(proc, t->Pointer.elem);
 					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), gep);
 				} break;
+				case Type_Atomic: {
+					ir_emit_comment(proc, str_lit("Type_Info_Atomic"));
+					tag = ir_emit_conv(proc, ti_ptr, t_type_info_atomic_ptr);
+					irValue *gep = ir_get_type_info_ptr(proc, t->Atomic.elem);
+					ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 2), gep);
+				} break;
 				case Type_Array: {
 					ir_emit_comment(proc, str_lit("Type_Info_Array"));
 					tag = ir_emit_conv(proc, ti_ptr, t_type_info_array_ptr);

+ 18 - 0
src/ir_print.c

@@ -197,6 +197,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 		ir_print_type(f, m, t->Pointer.elem);
 		ir_fprintf(f, "*");
 		return;
+	case Type_Atomic:
+		ir_print_type(f, m, t->Atomic.elem);
+		return;
 	case Type_Array:
 		ir_fprintf(f, "[%lld x ", t->Array.count);
 		ir_print_type(f, m, t->Array.elem);
@@ -775,6 +778,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 	case irInstr_Store: {
 		Type *type = type_deref(ir_type(instr->Store.address));
 		ir_fprintf(f, "store ");
+		if (instr->Store.atomic) {
+			ir_fprintf(f, "atomic ");
+		}
 		ir_print_type(f, m, type);
 		ir_fprintf(f, " ");
 		ir_print_value(f, m, instr->Store.value, type);
@@ -782,17 +788,29 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 		ir_print_type(f, m, type);
 		ir_fprintf(f, "* ");
 		ir_print_value(f, m, instr->Store.address, type);
+		if (is_type_atomic(type)) {
+			// TODO(bill): Do ordering
+			ir_fprintf(f, " unordered");
+			ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
+		}
 		ir_fprintf(f, "\n");
 	} break;
 
 	case irInstr_Load: {
 		Type *type = instr->Load.type;
 		ir_fprintf(f, "%%%d = load ", value->index);
+		if (is_type_atomic(type)) {
+			ir_fprintf(f, "atomic ");
+		}
 		ir_print_type(f, m, type);
 		ir_fprintf(f, ", ");
 		ir_print_type(f, m, type);
 		ir_fprintf(f, "* ");
 		ir_print_value(f, m, instr->Load.address, type);
+		if (is_type_atomic(type)) {
+			// TODO(bill): Do ordering
+			ir_fprintf(f, " unordered");
+		}
 		ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
 	} break;
 

+ 21 - 0
src/parser.c

@@ -351,6 +351,10 @@ AST_NODE_KIND(_TypeBegin, "", i32) \
 		Token token; \
 		AstNode *type; \
 	}) \
+	AST_NODE_KIND(AtomicType, "atomic type", struct { \
+		Token token; \
+		AstNode *type; \
+	}) \
 	AST_NODE_KIND(ArrayType, "array type", struct { \
 		Token token; \
 		AstNode *count; \
@@ -530,6 +534,7 @@ Token ast_node_token(AstNode *node) {
 	case AstNode_HelperType:       return node->HelperType.token;
 	case AstNode_ProcType:         return node->ProcType.token;
 	case AstNode_PointerType:      return node->PointerType.token;
+	case AstNode_AtomicType:       return node->AtomicType.token;
 	case AstNode_ArrayType:        return node->ArrayType.token;
 	case AstNode_DynamicArrayType: return node->DynamicArrayType.token;
 	case AstNode_VectorType:       return node->VectorType.token;
@@ -1046,6 +1051,13 @@ AstNode *ast_pointer_type(AstFile *f, Token token, AstNode *type) {
 	return result;
 }
 
+AstNode *ast_atomic_type(AstFile *f, Token token, AstNode *type) {
+	AstNode *result = make_ast_node(f, AstNode_AtomicType);
+	result->AtomicType.token = token;
+	result->AtomicType.type = type;
+	return result;
+}
+
 AstNode *ast_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem) {
 	AstNode *result = make_ast_node(f, AstNode_ArrayType);
 	result->ArrayType.token = token;
@@ -1331,6 +1343,9 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) {
 	case AstNode_PointerType:
 		return is_semicolon_optional_for_node(f, s->PointerType.type);
 
+	case AstNode_AtomicType:
+		return is_semicolon_optional_for_node(f, s->AtomicType.type);
+
 	case AstNode_StructType:
 	case AstNode_UnionType:
 	case AstNode_RawUnionType:
@@ -2673,6 +2688,12 @@ AstNode *parse_type_or_ident(AstFile *f) {
 		return ast_pointer_type(f, token, elem);
 	}
 
+	case Token_atomic: {
+		Token token = expect_token(f, Token_atomic);
+		AstNode *elem = parse_type(f);
+		return ast_atomic_type(f, token, elem);
+	}
+
 	case Token_OpenBracket: {
 		Token token = expect_token(f, Token_OpenBracket);
 		AstNode *count_expr = NULL;

+ 1 - 0
src/tokenizer.c

@@ -115,6 +115,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_asm,            "asm"),                 \
 	TOKEN_KIND(Token_yield,          "yield"),               \
 	TOKEN_KIND(Token_await,          "await"),               \
+	TOKEN_KIND(Token_atomic,         "atomic"),              \
 TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
 	TOKEN_KIND(Token_Count, "")
 

+ 43 - 3
src/types.c

@@ -110,6 +110,7 @@ typedef struct TypeRecord {
 #define TYPE_KINDS                                        \
 	TYPE_KIND(Basic,   BasicType)                         \
 	TYPE_KIND(Pointer, struct { Type *elem; })            \
+	TYPE_KIND(Atomic,  struct { Type *elem; })            \
 	TYPE_KIND(Array,   struct { Type *elem; i64 count; }) \
 	TYPE_KIND(DynamicArray, struct { Type *elem; })       \
 	TYPE_KIND(Vector,  struct { Type *elem; i64 count; }) \
@@ -314,6 +315,7 @@ gb_global Type *t_type_info_any           = NULL;
 gb_global Type *t_type_info_string        = NULL;
 gb_global Type *t_type_info_boolean       = NULL;
 gb_global Type *t_type_info_pointer       = NULL;
+gb_global Type *t_type_info_atomic        = NULL;
 gb_global Type *t_type_info_procedure     = NULL;
 gb_global Type *t_type_info_array         = NULL;
 gb_global Type *t_type_info_dynamic_array = NULL;
@@ -335,6 +337,7 @@ gb_global Type *t_type_info_any_ptr           = NULL;
 gb_global Type *t_type_info_string_ptr        = NULL;
 gb_global Type *t_type_info_boolean_ptr       = NULL;
 gb_global Type *t_type_info_pointer_ptr       = NULL;
+gb_global Type *t_type_info_atomic_ptr        = NULL;
 gb_global Type *t_type_info_procedure_ptr     = NULL;
 gb_global Type *t_type_info_array_ptr         = NULL;
 gb_global Type *t_type_info_dynamic_array_ptr = NULL;
@@ -393,7 +396,31 @@ Type *base_enum_type(Type *t) {
 }
 
 Type *core_type(Type *t) {
-	return base_type(base_enum_type(t));
+	for (;;) {
+		if (t == NULL) {
+			break;
+		}
+
+		switch (t->kind) {
+		case Type_Named:
+			if (t == t->Named.base) {
+				return t_invalid;
+			}
+			t = t->Named.base;
+			continue;
+		case Type_Record:
+			if (t->Record.kind == TypeRecord_Enum) {
+				t = t->Record.enum_base_type;
+				continue;
+			}
+			break;
+		case Type_Atomic:
+			t = t->Atomic.elem;
+			continue;
+		}
+		break;
+	}
+	return t;
 }
 
 void set_base_type(Type *t, Type *base) {
@@ -423,6 +450,12 @@ Type *make_type_pointer(gbAllocator a, Type *elem) {
 	return t;
 }
 
+Type *make_type_atomic(gbAllocator a, Type *elem) {
+	Type *t = alloc_type(a, Type_Atomic);
+	t->Atomic.elem = elem;
+	return t;
+}
+
 Type *make_type_array(gbAllocator a, Type *elem, i64 count) {
 	Type *t = alloc_type(a, Type_Array);
 	t->Array.elem = elem;
@@ -532,8 +565,6 @@ Type *make_type_map(gbAllocator a, i64 count, Type *key, Type *value) {
 
 
 
-
-
 Type *type_deref(Type *t) {
 	if (t != NULL) {
 		Type *bt = base_type(t);
@@ -682,6 +713,10 @@ bool is_type_pointer(Type *t) {
 	}
 	return t->kind == Type_Pointer;
 }
+bool is_type_atomic(Type *t) {
+	t = base_type(t);
+	return t->kind == Type_Atomic;
+}
 bool is_type_tuple(Type *t) {
 	t = base_type(t);
 	return t->kind == Type_Tuple;
@@ -1985,6 +2020,11 @@ gbString write_type_to_string(gbString str, Type *type) {
 		str = write_type_to_string(str, type->Pointer.elem);
 		break;
 
+	case Type_Atomic:
+		str = gb_string_appendc(str, "atomic ");
+		str = write_type_to_string(str, type->Atomic.elem);
+		break;
+
 	case Type_Array:
 		str = gb_string_appendc(str, gb_bprintf("[%d]", cast(int)type->Array.count));
 		str = write_type_to_string(str, type->Array.elem);