Commit 278829fc2c

Andrew Kelley <superjoe30@gmail.com>
2018-07-14 21:45:15
self-hosted: adding a fn to an llvm module
1 parent 91636f1
src-self-hosted/codegen.zig
@@ -0,0 +1,61 @@
+const std = @import("std");
+// TODO codegen pretends that Module is renamed to Build because I plan to
+// do that refactor at some point
+const Build = @import("module.zig").Module;
+// we go through llvm instead of c for 2 reasons:
+// 1. to avoid accidentally calling the non-thread-safe functions
+// 2. patch up some of the types to remove nullability
+const llvm = @import("llvm.zig");
+const ir = @import("ir.zig");
+const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
+const event = std.event;
+
+pub async fn renderToLlvm(build: *Build, fn_val: *Value.Fn, code: *ir.Code) !void {
+    fn_val.base.ref();
+    defer fn_val.base.deref(build);
+    defer code.destroy(build.a());
+
+    const llvm_handle = try build.event_loop_local.getAnyLlvmContext();
+    defer llvm_handle.release(build.event_loop_local);
+
+    const context = llvm_handle.node.data;
+
+    const module = llvm.ModuleCreateWithNameInContext(build.name.ptr(), context) orelse return error.OutOfMemory;
+    defer llvm.DisposeModule(module);
+
+    const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;
+    defer llvm.DisposeBuilder(builder);
+
+    var cunit = CompilationUnit{
+        .build = build,
+        .module = module,
+        .builder = builder,
+        .context = context,
+        .lock = event.Lock.init(build.loop),
+    };
+
+    try renderToLlvmModule(&cunit, fn_val, code);
+
+    if (build.verbose_llvm_ir) {
+        llvm.DumpModule(cunit.module);
+    }
+}
+
+pub const CompilationUnit = struct {
+    build: *Build,
+    module: llvm.ModuleRef,
+    builder: llvm.BuilderRef,
+    context: llvm.ContextRef,
+    lock: event.Lock,
+
+    fn a(self: *CompilationUnit) *std.mem.Allocator {
+        return self.build.a();
+    }
+};
+
+pub fn renderToLlvmModule(cunit: *CompilationUnit, fn_val: *Value.Fn, code: *ir.Code) !void {
+    // TODO audit more of codegen.cpp:fn_llvm_value and port more logic
+    const llvm_fn_type = try fn_val.base.typeof.getLlvmType(cunit);
+    const llvm_fn = llvm.AddFunction(cunit.module, fn_val.symbol_name.ptr(), llvm_fn_type);
+}
src-self-hosted/ir.zig
@@ -375,15 +375,8 @@ pub const Instruction = struct {
 
         pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction {
             const target = try self.params.target.getAsParam();
-
             try ira.src_implicit_return_type_list.append(target);
-
-            return ira.irb.build(
-                AddImplicitReturnType,
-                self.base.scope,
-                self.base.span,
-                Params{ .target = target },
-            );
+            return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
         }
     };
 };
src-self-hosted/llvm.zig
@@ -2,10 +2,27 @@ const builtin = @import("builtin");
 const c = @import("c.zig");
 const assert = @import("std").debug.assert;
 
-pub const ValueRef = removeNullability(c.LLVMValueRef);
-pub const ModuleRef = removeNullability(c.LLVMModuleRef);
-pub const ContextRef = removeNullability(c.LLVMContextRef);
 pub const BuilderRef = removeNullability(c.LLVMBuilderRef);
+pub const ContextRef = removeNullability(c.LLVMContextRef);
+pub const ModuleRef = removeNullability(c.LLVMModuleRef);
+pub const ValueRef = removeNullability(c.LLVMValueRef);
+pub const TypeRef = removeNullability(c.LLVMTypeRef);
+
+pub const AddFunction = c.LLVMAddFunction;
+pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;
+pub const DisposeBuilder = c.LLVMDisposeBuilder;
+pub const DisposeModule = c.LLVMDisposeModule;
+pub const DumpModule = c.LLVMDumpModule;
+pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
+pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
+
+pub const FunctionType = LLVMFunctionType;
+extern fn LLVMFunctionType(
+    ReturnType: TypeRef,
+    ParamTypes: [*]TypeRef,
+    ParamCount: c_uint,
+    IsVarArg: c_int,
+) ?TypeRef;
 
 fn removeNullability(comptime T: type) type {
     comptime assert(@typeId(T) == builtin.TypeId.Optional);
src-self-hosted/main.zig
@@ -14,6 +14,7 @@ const c = @import("c.zig");
 const introspect = @import("introspect.zig");
 const Args = arg.Args;
 const Flag = arg.Flag;
+const EventLoopLocal = @import("module.zig").EventLoopLocal;
 const Module = @import("module.zig").Module;
 const Target = @import("target.zig").Target;
 const errmsg = @import("errmsg.zig");
@@ -386,9 +387,13 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
 
     var loop: event.Loop = undefined;
     try loop.initMultiThreaded(allocator);
+    defer loop.deinit();
+
+    var event_loop_local = EventLoopLocal.init(&loop);
+    defer event_loop_local.deinit();
 
     var module = try Module.create(
-        &loop,
+        &event_loop_local,
         root_name,
         root_source_file,
         Target.Native,
src-self-hosted/module.zig
@@ -25,14 +25,58 @@ const ParsedFile = @import("parsed_file.zig").ParsedFile;
 const Value = @import("value.zig").Value;
 const Type = Value.Type;
 const Span = errmsg.Span;
+const codegen = @import("codegen.zig");
+
+/// Data that is local to the event loop.
+pub const EventLoopLocal = struct {
+    loop: *event.Loop,
+    llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
+
+    fn init(loop: *event.Loop) EventLoopLocal {
+        return EventLoopLocal{
+            .loop = loop,
+            .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
+        };
+    }
+
+    fn deinit(self: *EventLoopLocal) void {
+        while (self.llvm_handle_pool.pop()) |node| {
+            c.LLVMContextDispose(node.data);
+            self.loop.allocator.destroy(node);
+        }
+    }
+
+    /// Gets an exclusive handle on any LlvmContext.
+    /// Caller must release the handle when done.
+    pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle {
+        if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
+
+        const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
+        errdefer c.LLVMContextDispose(context_ref);
+
+        const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node{
+            .next = undefined,
+            .data = context_ref,
+        });
+        errdefer self.loop.allocator.destroy(node);
+
+        return LlvmHandle{ .node = node };
+    }
+};
+
+pub const LlvmHandle = struct {
+    node: *std.atomic.Stack(llvm.ContextRef).Node,
+
+    pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void {
+        event_loop_local.llvm_handle_pool.push(self.node);
+    }
+};
 
 pub const Module = struct {
+    event_loop_local: *EventLoopLocal,
     loop: *event.Loop,
     name: Buffer,
     root_src_path: ?[]const u8,
-    llvm_module: llvm.ModuleRef,
-    context: llvm.ContextRef,
-    builder: llvm.BuilderRef,
     target: Target,
     build_mode: builtin.Mode,
     zig_lib_dir: []const u8,
@@ -187,7 +231,7 @@ pub const Module = struct {
     };
 
     pub fn create(
-        loop: *event.Loop,
+        event_loop_local: *EventLoopLocal,
         name: []const u8,
         root_src_path: ?[]const u8,
         target: *const Target,
@@ -196,29 +240,20 @@ pub const Module = struct {
         zig_lib_dir: []const u8,
         cache_dir: []const u8,
     ) !*Module {
+        const loop = event_loop_local.loop;
+
         var name_buffer = try Buffer.init(loop.allocator, name);
         errdefer name_buffer.deinit();
 
-        const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
-        errdefer c.LLVMContextDispose(context);
-
-        const llvm_module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
-        errdefer c.LLVMDisposeModule(llvm_module);
-
-        const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
-        errdefer c.LLVMDisposeBuilder(builder);
-
         const events = try event.Channel(Event).create(loop, 0);
         errdefer events.destroy();
 
         const module = try loop.allocator.create(Module{
             .loop = loop,
+            .event_loop_local = event_loop_local,
             .events = events,
             .name = name_buffer,
             .root_src_path = root_src_path,
-            .llvm_module = llvm_module,
-            .context = context,
-            .builder = builder,
             .target = target.*,
             .kind = kind,
             .build_mode = build_mode,
@@ -290,7 +325,7 @@ pub const Module = struct {
                 .base = Value{
                     .id = Value.Id.Type,
                     .typeof = undefined,
-                    .ref_count = 3, // 3 because it references itself twice
+                    .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
                 },
                 .id = builtin.TypeId.Type,
             },
@@ -305,7 +340,7 @@ pub const Module = struct {
                 .base = Value{
                     .id = Value.Id.Type,
                     .typeof = &Type.MetaType.get(module).base,
-                    .ref_count = 1,
+                    .ref_count = std.atomic.Int(usize).init(1),
                 },
                 .id = builtin.TypeId.Void,
             },
@@ -317,7 +352,7 @@ pub const Module = struct {
                 .base = Value{
                     .id = Value.Id.Type,
                     .typeof = &Type.MetaType.get(module).base,
-                    .ref_count = 1,
+                    .ref_count = std.atomic.Int(usize).init(1),
                 },
                 .id = builtin.TypeId.NoReturn,
             },
@@ -329,7 +364,7 @@ pub const Module = struct {
                 .base = Value{
                     .id = Value.Id.Type,
                     .typeof = &Type.MetaType.get(module).base,
-                    .ref_count = 1,
+                    .ref_count = std.atomic.Int(usize).init(1),
                 },
                 .id = builtin.TypeId.Bool,
             },
@@ -340,7 +375,7 @@ pub const Module = struct {
             .base = Value{
                 .id = Value.Id.Void,
                 .typeof = &Type.Void.get(module).base,
-                .ref_count = 1,
+                .ref_count = std.atomic.Int(usize).init(1),
             },
         });
         errdefer module.a().destroy(module.void_value);
@@ -349,7 +384,7 @@ pub const Module = struct {
             .base = Value{
                 .id = Value.Id.Bool,
                 .typeof = &Type.Bool.get(module).base,
-                .ref_count = 1,
+                .ref_count = std.atomic.Int(usize).init(1),
             },
             .x = true,
         });
@@ -359,7 +394,7 @@ pub const Module = struct {
             .base = Value{
                 .id = Value.Id.Bool,
                 .typeof = &Type.Bool.get(module).base,
-                .ref_count = 1,
+                .ref_count = std.atomic.Int(usize).init(1),
             },
             .x = false,
         });
@@ -369,16 +404,12 @@ pub const Module = struct {
             .base = Value{
                 .id = Value.Id.NoReturn,
                 .typeof = &Type.NoReturn.get(module).base,
-                .ref_count = 1,
+                .ref_count = std.atomic.Int(usize).init(1),
             },
         });
         errdefer module.a().destroy(module.noreturn_value);
     }
 
-    fn dump(self: *Module) void {
-        c.LLVMDumpModule(self.module);
-    }
-
     pub fn destroy(self: *Module) void {
         self.noreturn_value.base.deref(self);
         self.void_value.base.deref(self);
@@ -389,9 +420,6 @@ pub const Module = struct {
         self.meta_type.base.base.deref(self);
 
         self.events.destroy();
-        c.LLVMDisposeBuilder(self.builder);
-        c.LLVMDisposeModule(self.llvm_module);
-        c.LLVMContextDispose(self.context);
         self.name.deinit();
 
         self.a().destroy(self);
@@ -657,10 +685,19 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void {
     const fndef_scope = try Scope.FnDef.create(module, fn_decl.base.parent_scope);
     defer fndef_scope.base.deref(module);
 
-    const fn_type = try Type.Fn.create(module);
+    // TODO actually look at the return type of the AST
+    const return_type = &Type.Void.get(module).base;
+    defer return_type.base.deref(module);
+
+    const is_var_args = false;
+    const params = ([*]Type.Fn.Param)(undefined)[0..0];
+    const fn_type = try Type.Fn.create(module, return_type, params, is_var_args);
     defer fn_type.base.base.deref(module);
 
-    const fn_val = try Value.Fn.create(module, fn_type, fndef_scope);
+    var symbol_name = try std.Buffer.init(module.a(), fn_decl.base.name);
+    errdefer symbol_name.deinit();
+
+    const fn_val = try Value.Fn.create(module, fn_type, fndef_scope, symbol_name);
     defer fn_val.base.deref(module);
 
     fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
@@ -674,6 +711,7 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void {
     ) catch unreachable)) catch |err| switch (err) {
         // This poison value should not cause the errdefers to run. It simply means
         // that self.compile_errors is populated.
+        // TODO https://github.com/ziglang/zig/issues/769
         error.SemanticAnalysisFailed => return {},
         else => return err,
     };
@@ -692,14 +730,18 @@ async fn generateDeclFn(module: *Module, fn_decl: *Decl.Fn) !void {
     ) catch unreachable)) catch |err| switch (err) {
         // This poison value should not cause the errdefers to run. It simply means
         // that self.compile_errors is populated.
+        // TODO https://github.com/ziglang/zig/issues/769
         error.SemanticAnalysisFailed => return {},
         else => return err,
     };
-    defer analyzed_code.destroy(module.a());
+    errdefer analyzed_code.destroy(module.a());
 
     if (module.verbose_ir) {
         std.debug.warn("analyzed:\n");
         analyzed_code.dump();
     }
-    // TODO now render to LLVM module
+
+    // Kick off rendering to LLVM module, but it doesn't block the fn decl
+    // analysis from being complete.
+    try module.build_group.call(codegen.renderToLlvm, module, fn_val, analyzed_code);
 }
src-self-hosted/test.zig
@@ -6,6 +6,7 @@ const Module = @import("module.zig").Module;
 const introspect = @import("introspect.zig");
 const assertOrPanic = std.debug.assertOrPanic;
 const errmsg = @import("errmsg.zig");
+const EventLoopLocal = @import("module.zig").EventLoopLocal;
 
 test "compile errors" {
     var ctx: TestContext = undefined;
@@ -22,6 +23,7 @@ const allocator = std.heap.c_allocator;
 
 pub const TestContext = struct {
     loop: std.event.Loop,
+    event_loop_local: EventLoopLocal,
     zig_lib_dir: []u8,
     zig_cache_dir: []u8,
     file_index: std.atomic.Int(usize),
@@ -34,6 +36,7 @@ pub const TestContext = struct {
         self.* = TestContext{
             .any_err = {},
             .loop = undefined,
+            .event_loop_local = undefined,
             .zig_lib_dir = undefined,
             .zig_cache_dir = undefined,
             .group = undefined,
@@ -43,6 +46,9 @@ pub const TestContext = struct {
         try self.loop.initMultiThreaded(allocator);
         errdefer self.loop.deinit();
 
+        self.event_loop_local = EventLoopLocal.init(&self.loop);
+        errdefer self.event_loop_local.deinit();
+
         self.group = std.event.Group(error!void).init(&self.loop);
         errdefer self.group.cancelAll();
 
@@ -60,6 +66,7 @@ pub const TestContext = struct {
         std.os.deleteTree(allocator, tmp_dir_name) catch {};
         allocator.free(self.zig_cache_dir);
         allocator.free(self.zig_lib_dir);
+        self.event_loop_local.deinit();
         self.loop.deinit();
     }
 
@@ -83,7 +90,7 @@ pub const TestContext = struct {
         msg: []const u8,
     ) !void {
         var file_index_buf: [20]u8 = undefined;
-        const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.next());
+        const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
         const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
 
         if (std.os.path.dirname(file1_path)) |dirname| {
@@ -94,7 +101,7 @@ pub const TestContext = struct {
         try std.io.writeFile(allocator, file1_path, source);
 
         var module = try Module.create(
-            &self.loop,
+            &self.event_loop_local,
             "test",
             file1_path,
             Target.Native,
src-self-hosted/type.zig
@@ -1,7 +1,10 @@
+const std = @import("std");
 const builtin = @import("builtin");
 const Scope = @import("scope.zig").Scope;
 const Module = @import("module.zig").Module;
 const Value = @import("value.zig").Value;
+const llvm = @import("llvm.zig");
+const CompilationUnit = @import("codegen.zig").CompilationUnit;
 
 pub const Type = struct {
     base: Value,
@@ -39,6 +42,36 @@ pub const Type = struct {
         }
     }
 
+    pub fn getLlvmType(base: *Type, cunit: *CompilationUnit) (error{OutOfMemory}!llvm.TypeRef) {
+        switch (base.id) {
+            Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(cunit),
+            Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(cunit),
+            Id.Type => unreachable,
+            Id.Void => unreachable,
+            Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(cunit),
+            Id.NoReturn => unreachable,
+            Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(cunit),
+            Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(cunit),
+            Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(cunit),
+            Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(cunit),
+            Id.ComptimeFloat => unreachable,
+            Id.ComptimeInt => unreachable,
+            Id.Undefined => unreachable,
+            Id.Null => unreachable,
+            Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(cunit),
+            Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(cunit),
+            Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(cunit),
+            Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(cunit),
+            Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(cunit),
+            Id.Namespace => unreachable,
+            Id.Block => unreachable,
+            Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(cunit),
+            Id.ArgTuple => unreachable,
+            Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(cunit),
+            Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(cunit),
+        }
+    }
+
     pub fn dump(base: *const Type) void {
         std.debug.warn("{}", @tagName(base.id));
     }
@@ -54,27 +87,72 @@ pub const Type = struct {
         pub fn destroy(self: *Struct, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Struct, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
 
     pub const Fn = struct {
         base: Type,
+        return_type: *Type,
+        params: []Param,
+        is_var_args: bool,
 
-        pub fn create(module: *Module) !*Fn {
-            return module.a().create(Fn{
+        pub const Param = struct {
+            is_noalias: bool,
+            typeof: *Type,
+        };
+
+        pub fn create(module: *Module, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
+            const result = try module.a().create(Fn{
                 .base = Type{
                     .base = Value{
                         .id = Value.Id.Type,
                         .typeof = &MetaType.get(module).base,
-                        .ref_count = 1,
+                        .ref_count = std.atomic.Int(usize).init(1),
                     },
                     .id = builtin.TypeId.Fn,
                 },
+                .return_type = return_type,
+                .params = params,
+                .is_var_args = is_var_args,
             });
+            errdefer module.a().destroy(result);
+
+            result.return_type.base.ref();
+            for (result.params) |param| {
+                param.typeof.base.ref();
+            }
+            return result;
         }
 
         pub fn destroy(self: *Fn, module: *Module) void {
+            self.return_type.base.deref(module);
+            for (self.params) |param| {
+                param.typeof.base.deref(module);
+            }
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Fn, cunit: *CompilationUnit) !llvm.TypeRef {
+            const llvm_return_type = switch (self.return_type.id) {
+                Type.Id.Void => llvm.VoidTypeInContext(cunit.context) orelse return error.OutOfMemory,
+                else => try self.return_type.getLlvmType(cunit),
+            };
+            const llvm_param_types = try cunit.a().alloc(llvm.TypeRef, self.params.len);
+            defer cunit.a().free(llvm_param_types);
+            for (llvm_param_types) |*llvm_param_type, i| {
+                llvm_param_type.* = try self.params[i].typeof.getLlvmType(cunit);
+            }
+
+            return llvm.FunctionType(
+                llvm_return_type,
+                llvm_param_types.ptr,
+                @intCast(c_uint, llvm_param_types.len),
+                @boolToInt(self.is_var_args),
+            ) orelse error.OutOfMemory;
+        }
     };
 
     pub const MetaType = struct {
@@ -118,6 +196,10 @@ pub const Type = struct {
         pub fn destroy(self: *Bool, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Bool, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
 
     pub const NoReturn = struct {
@@ -140,6 +222,10 @@ pub const Type = struct {
         pub fn destroy(self: *Int, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Int, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
 
     pub const Float = struct {
@@ -148,6 +234,10 @@ pub const Type = struct {
         pub fn destroy(self: *Float, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Float, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
     pub const Pointer = struct {
         base: Type,
@@ -180,14 +270,24 @@ pub const Type = struct {
         ) *Pointer {
             @panic("TODO get pointer");
         }
+
+        pub fn getLlvmType(self: *Pointer, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
+
     pub const Array = struct {
         base: Type,
 
         pub fn destroy(self: *Array, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Array, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
+
     pub const ComptimeFloat = struct {
         base: Type,
 
@@ -195,6 +295,7 @@ pub const Type = struct {
             module.a().destroy(self);
         }
     };
+
     pub const ComptimeInt = struct {
         base: Type,
 
@@ -202,6 +303,7 @@ pub const Type = struct {
             module.a().destroy(self);
         }
     };
+
     pub const Undefined = struct {
         base: Type,
 
@@ -209,6 +311,7 @@ pub const Type = struct {
             module.a().destroy(self);
         }
     };
+
     pub const Null = struct {
         base: Type,
 
@@ -216,41 +319,67 @@ pub const Type = struct {
             module.a().destroy(self);
         }
     };
+
     pub const Optional = struct {
         base: Type,
 
         pub fn destroy(self: *Optional, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Optional, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
+
     pub const ErrorUnion = struct {
         base: Type,
 
         pub fn destroy(self: *ErrorUnion, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *ErrorUnion, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
+
     pub const ErrorSet = struct {
         base: Type,
 
         pub fn destroy(self: *ErrorSet, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *ErrorSet, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
+
     pub const Enum = struct {
         base: Type,
 
         pub fn destroy(self: *Enum, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Enum, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
+
     pub const Union = struct {
         base: Type,
 
         pub fn destroy(self: *Union, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Union, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
+
     pub const Namespace = struct {
         base: Type,
 
@@ -273,6 +402,10 @@ pub const Type = struct {
         pub fn destroy(self: *BoundFn, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *BoundFn, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
 
     pub const ArgTuple = struct {
@@ -289,6 +422,10 @@ pub const Type = struct {
         pub fn destroy(self: *Opaque, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Opaque, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
 
     pub const Promise = struct {
@@ -297,5 +434,9 @@ pub const Type = struct {
         pub fn destroy(self: *Promise, module: *Module) void {
             module.a().destroy(self);
         }
+
+        pub fn getLlvmType(self: *Promise, cunit: *CompilationUnit) llvm.TypeRef {
+            @panic("TODO");
+        }
     };
 };
src-self-hosted/value.zig
@@ -8,15 +8,16 @@ const Module = @import("module.zig").Module;
 pub const Value = struct {
     id: Id,
     typeof: *Type,
-    ref_count: usize,
+    ref_count: std.atomic.Int(usize),
 
+    /// Thread-safe
     pub fn ref(base: *Value) void {
-        base.ref_count += 1;
+        _ = base.ref_count.incr();
     }
 
+    /// Thread-safe
     pub fn deref(base: *Value, module: *Module) void {
-        base.ref_count -= 1;
-        if (base.ref_count == 0) {
+        if (base.ref_count.decr() == 1) {
             base.typeof.base.deref(module);
             switch (base.id) {
                 Id.Type => @fieldParentPtr(Type, "base", base).destroy(module),
@@ -52,6 +53,10 @@ pub const Value = struct {
     pub const Fn = struct {
         base: Value,
 
+        /// The main external name that is used in the .o file.
+        /// TODO https://github.com/ziglang/zig/issues/265
+        symbol_name: std.Buffer,
+
         /// parent should be the top level decls or container decls
         fndef_scope: *Scope.FnDef,
 
@@ -62,16 +67,18 @@ pub const Value = struct {
         block_scope: *Scope.Block,
 
         /// Creates a Fn value with 1 ref
-        pub fn create(module: *Module, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef) !*Fn {
+        /// Takes ownership of symbol_name
+        pub fn create(module: *Module, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn {
             const self = try module.a().create(Fn{
                 .base = Value{
                     .id = Value.Id.Fn,
                     .typeof = &fn_type.base,
-                    .ref_count = 1,
+                    .ref_count = std.atomic.Int(usize).init(1),
                 },
                 .fndef_scope = fndef_scope,
                 .child_scope = &fndef_scope.base,
                 .block_scope = undefined,
+                .symbol_name = symbol_name,
             });
             fn_type.base.base.ref();
             fndef_scope.fn_val = self;
@@ -81,6 +88,7 @@ pub const Value = struct {
 
         pub fn destroy(self: *Fn, module: *Module) void {
             self.fndef_scope.base.deref(module);
+            self.symbol_name.deinit();
             module.a().destroy(self);
         }
     };
std/atomic/int.zig
@@ -4,16 +4,26 @@ const AtomicOrder = builtin.AtomicOrder;
 /// Thread-safe, lock-free integer
 pub fn Int(comptime T: type) type {
     return struct {
-        value: T,
+        unprotected_value: T,
 
         pub const Self = this;
 
         pub fn init(init_val: T) Self {
-            return Self{ .value = init_val };
+            return Self{ .unprotected_value = init_val };
         }
 
-        pub fn next(self: *Self) T {
-            return @atomicRmw(T, &self.value, builtin.AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
+        /// Returns previous value
+        pub fn incr(self: *Self) T {
+            return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
+        }
+
+        /// Returns previous value
+        pub fn decr(self: *Self) T {
+            return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
+        }
+
+        pub fn get(self: *Self) T {
+            return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst);
         }
     };
 }
std/event/loop.zig
@@ -101,7 +101,6 @@ pub const Loop = struct {
         errdefer self.deinitOsData();
     }
 
-    /// must call stop before deinit
     pub fn deinit(self: *Loop) void {
         self.deinitOsData();
         self.allocator.free(self.extra_threads);