From 363f4facea7fac2d6cfeab9d1d276ecd8e8e4df0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Jul 2018 00:04:12 -0400 Subject: [PATCH] self-hosted: generate LLVM IR for simple function --- src-self-hosted/codegen.zig | 158 +++++++++++++++++++++++++++++++- src-self-hosted/compilation.zig | 6 +- src-self-hosted/ir.zig | 56 +++++++++-- src-self-hosted/llvm.zig | 64 ++++++++++++- src-self-hosted/scope.zig | 32 +++++++ src-self-hosted/type.zig | 75 +++++++++++++++ src-self-hosted/value.zig | 22 +++++ std/event/future.zig | 10 ++ 8 files changed, 412 insertions(+), 11 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index a07485e74e..698f1e5b45 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -8,6 +8,7 @@ const ir = @import("ir.zig"); const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const event = std.event; +const assert = std.debug.assert; pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { fn_val.base.ref(); @@ -35,9 +36,23 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) try renderToLlvmModule(&ofile, fn_val, code); + // TODO module level assembly + //if (buf_len(&g->global_asm) != 0) { + // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); + //} + + // TODO + //ZigLLVMDIBuilderFinalize(g->dbuilder); + if (comp.verbose_llvm_ir) { llvm.DumpModule(ofile.module); } + + // verify the llvm module when safety is on + if (std.debug.runtime_safety) { + var error_ptr: ?[*]u8 = null; + _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); + } } pub const ObjectFile = struct { @@ -55,5 +70,146 @@ pub const ObjectFile = struct { pub fn renderToLlvmModule(ofile: *ObjectFile, 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(ofile); - const llvm_fn = llvm.AddFunction(ofile.module, fn_val.symbol_name.ptr(), llvm_fn_type); + const llvm_fn = llvm.AddFunction( + ofile.module, + fn_val.symbol_name.ptr(), + llvm_fn_type, + ) orelse return error.OutOfMemory; + + const want_fn_safety = fn_val.block_scope.safety.get(ofile.comp); + if (want_fn_safety and ofile.comp.haveLibC()) { + try addLLVMFnAttr(ofile, llvm_fn, "sspstrong"); + try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4"); + } + + // TODO + //if (fn_val.align_stack) |align_stack| { + // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack); + //} + + const fn_type = fn_val.base.typeof.cast(Type.Fn).?; + + try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); + //add_uwtable_attr(g, fn_table_entry->llvm_value); + try addLLVMFnAttr(ofile, llvm_fn, "nobuiltin"); + + //if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) { + // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim", "true"); + // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim-non-leaf", nullptr); + //} + + //if (fn_table_entry->section_name) { + // LLVMSetSection(fn_table_entry->llvm_value, buf_ptr(fn_table_entry->section_name)); + //} + //if (fn_table_entry->align_bytes > 0) { + // LLVMSetAlignment(fn_table_entry->llvm_value, (unsigned)fn_table_entry->align_bytes); + //} else { + // // We'd like to set the best alignment for the function here, but on Darwin LLVM gives + // // "Cannot getTypeInfo() on a type that is unsized!" assertion failure when calling + // // any of the functions for getting alignment. Not specifying the alignment should + // // use the ABI alignment, which is fine. + //} + + //if (!type_has_bits(return_type)) { + // // nothing to do + //} else if (type_is_codegen_pointer(return_type)) { + // addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); + //} else if (handle_is_ptr(return_type) && + // calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc)) + //{ + // addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); + // addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); + //} + + // TODO set parameter attributes + + // TODO + //uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); + //if (err_ret_trace_arg_index != UINT32_MAX) { + // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); + //} + + const cur_ret_ptr = if (fn_type.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null; + + // build all basic blocks + for (code.basic_block_list.toSlice()) |bb| { + bb.llvm_block = llvm.AppendBasicBlockInContext( + ofile.context, + llvm_fn, + bb.name_hint, + ) orelse return error.OutOfMemory; + } + const entry_bb = code.basic_block_list.at(0); + llvm.PositionBuilderAtEnd(ofile.builder, entry_bb.llvm_block); + + llvm.ClearCurrentDebugLocation(ofile.builder); + + // TODO set up error return tracing + // TODO allocate temporary stack values + // TODO create debug variable declarations for variables and allocate all local variables + // TODO finishing error return trace setup. we have to do this after all the allocas. + // TODO create debug variable declarations for parameters + + for (code.basic_block_list.toSlice()) |current_block| { + llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block); + for (current_block.instruction_list.toSlice()) |instruction| { + if (instruction.ref_count == 0 and !instruction.hasSideEffects()) continue; + + instruction.llvm_value = try instruction.render(ofile, fn_val); + } + current_block.llvm_exit_block = llvm.GetInsertBlock(ofile.builder); + } +} + +fn addLLVMAttr( + ofile: *ObjectFile, + val: llvm.ValueRef, + attr_index: llvm.AttributeIndex, + attr_name: []const u8, +) !void { + const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len); + assert(kind_id != 0); + const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, 0) orelse return error.OutOfMemory; + llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); +} + +fn addLLVMAttrStr( + ofile: *ObjectFile, + val: llvm.ValueRef, + attr_index: llvm.AttributeIndex, + attr_name: []const u8, + attr_val: []const u8, +) !void { + const llvm_attr = llvm.CreateStringAttribute( + ofile.context, + attr_name.ptr, + @intCast(c_uint, attr_name.len), + attr_val.ptr, + @intCast(c_uint, attr_val.len), + ) orelse return error.OutOfMemory; + llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); +} + +fn addLLVMAttrInt( + val: llvm.ValueRef, + attr_index: llvm.AttributeIndex, + attr_name: []const u8, + attr_val: u64, +) !void { + const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len); + assert(kind_id != 0); + const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, attr_val) orelse return error.OutOfMemory; + llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); +} + +fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8) !void { + return addLLVMAttr(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name); +} + +fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: []const u8) !void { + return addLLVMAttrStr(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val); +} + +fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void { + return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val); } diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index cbda7861bc..1dbbf21206 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -606,6 +606,10 @@ pub const Compilation = struct { return error.Todo; } + pub fn haveLibC(self: *Compilation) bool { + return self.libc_link_lib != null; + } + pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib { const is_libc = mem.eql(u8, name, "c"); @@ -741,7 +745,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { analyzed_code.dump(); } - // Kick off rendering to LLVM comp, but it doesn't block the fn decl + // Kick off rendering to LLVM module, but it doesn't block the fn decl // analysis from being complete. try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 22d5a067a7..0e0a4f9bf3 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -10,6 +10,8 @@ const assert = std.debug.assert; const Token = std.zig.Token; const ParsedFile = @import("parsed_file.zig").ParsedFile; const Span = @import("errmsg.zig").Span; +const llvm = @import("llvm.zig"); +const ObjectFile = @import("codegen.zig").ObjectFile; pub const LVal = enum { None, @@ -61,6 +63,9 @@ pub const Instruction = struct { /// the instruction that this one derives from in analysis parent: ?*Instruction, + /// populated durign codegen + llvm_value: ?llvm.ValueRef, + pub fn cast(base: *Instruction, comptime T: type) ?*T { if (base.id == comptime typeToId(T)) { return @fieldParentPtr(T, "base", base); @@ -108,14 +113,25 @@ pub const Instruction = struct { inline while (i < @memberCount(Id)) : (i += 1) { if (base.id == @field(Id, @memberName(Id, i))) { const T = @field(Instruction, @memberName(Id, i)); - const new_inst = try @fieldParentPtr(T, "base", base).analyze(ira); - new_inst.linkToParent(base); - return new_inst; + return @fieldParentPtr(T, "base", base).analyze(ira); } } unreachable; } + pub fn render(base: *Instruction, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) { + switch (base.id) { + Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), + Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), + Id.Ref => @panic("TODO"), + Id.DeclVar => @panic("TODO"), + Id.CheckVoidStmt => @panic("TODO"), + Id.Phi => @panic("TODO"), + Id.Br => @panic("TODO"), + Id.AddImplicitReturnType => @panic("TODO"), + } + } + fn getAsParam(param: *Instruction) !*Instruction { const child = param.child orelse return error.SemanticAnalysisFailed; switch (child.val) { @@ -186,6 +202,10 @@ pub const Instruction = struct { new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() }; return new_inst; } + + pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { + return self.base.val.KnownValue.getLlvmConst(ofile); + } }; pub const Return = struct { @@ -214,6 +234,18 @@ pub const Instruction = struct { return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value }); } + + pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) ?llvm.ValueRef { + const value = self.params.return_value.llvm_value; + const return_type = self.params.return_value.getKnownType(); + + if (return_type.handleIsPtr()) { + @panic("TODO"); + } else { + _ = llvm.BuildRet(ofile.builder, value); + } + return null; + } }; pub const Ref = struct { @@ -387,12 +419,16 @@ pub const Variable = struct { pub const BasicBlock = struct { ref_count: usize, - name_hint: []const u8, + name_hint: [*]const u8, // must be a C string literal debug_id: usize, scope: *Scope, instruction_list: std.ArrayList(*Instruction), ref_instruction: ?*Instruction, + /// for codegen + llvm_block: llvm.BasicBlockRef, + llvm_exit_block: llvm.BasicBlockRef, + /// the basic block that is derived from this one in analysis child: ?*BasicBlock, @@ -426,7 +462,7 @@ pub const Code = struct { pub fn dump(self: *Code) void { var bb_i: usize = 0; for (self.basic_block_list.toSliceConst()) |bb| { - std.debug.warn("{}_{}:\n", bb.name_hint, bb.debug_id); + std.debug.warn("{s}_{}:\n", bb.name_hint, bb.debug_id); for (bb.instruction_list.toSliceConst()) |instr| { std.debug.warn(" "); instr.dump(); @@ -475,7 +511,7 @@ pub const Builder = struct { } /// No need to clean up resources thanks to the arena allocator. - pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: []const u8) !*BasicBlock { + pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock { const basic_block = try self.arena().create(BasicBlock{ .ref_count = 0, .name_hint = name_hint, @@ -485,6 +521,8 @@ pub const Builder = struct { .child = null, .parent = null, .ref_instruction = null, + .llvm_block = undefined, + .llvm_exit_block = undefined, }); self.next_debug_id += 1; return basic_block; @@ -600,7 +638,7 @@ pub const Builder = struct { if (block.label) |label| { block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena()); block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); - block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd"); + block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd"); block_scope.is_comptime = try irb.buildConstBool( parent_scope, Span.token(block.lbrace), @@ -777,6 +815,7 @@ pub const Builder = struct { .span = span, .child = null, .parent = null, + .llvm_value = undefined, }, .params = params, }); @@ -968,7 +1007,7 @@ pub async fn gen( var irb = try Builder.init(comp, parsed_file); errdefer irb.abort(); - const entry_block = try irb.createBasicBlock(scope, "Entry"); + const entry_block = try irb.createBasicBlock(scope, c"Entry"); entry_block.ref(); // Entry block gets a reference because we enter it to begin. try irb.setCursorAtEndAndAppendBlock(entry_block); @@ -1013,6 +1052,7 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co } const return_inst = try old_instruction.analyze(&ira); + return_inst.linkToParent(old_instruction); // Note: if we ever modify the above to handle error.CompileError by continuing analysis, // then here we want to check if ira.isCompTime() and return early if true diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index b815f75b05..13480dc2c6 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -2,29 +2,91 @@ const builtin = @import("builtin"); const c = @import("c.zig"); const assert = @import("std").debug.assert; +pub const AttributeIndex = c_uint; +pub const Bool = c_int; + 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 BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); +pub const AttributeRef = removeNullability(c.LLVMAttributeRef); +pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; pub const AddFunction = c.LLVMAddFunction; +pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; +pub const ConstInt = c.LLVMConstInt; +pub const ConstStringInContext = c.LLVMConstStringInContext; +pub const ConstStructInContext = c.LLVMConstStructInContext; pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; +pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; +pub const CreateStringAttribute = c.LLVMCreateStringAttribute; pub const DisposeBuilder = c.LLVMDisposeBuilder; pub const DisposeModule = c.LLVMDisposeModule; +pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; pub const DumpModule = c.LLVMDumpModule; +pub const FP128TypeInContext = c.LLVMFP128TypeInContext; +pub const FloatTypeInContext = c.LLVMFloatTypeInContext; +pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; +pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; +pub const HalfTypeInContext = c.LLVMHalfTypeInContext; +pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; +pub const Int128TypeInContext = c.LLVMInt128TypeInContext; +pub const Int16TypeInContext = c.LLVMInt16TypeInContext; +pub const Int1TypeInContext = c.LLVMInt1TypeInContext; +pub const Int32TypeInContext = c.LLVMInt32TypeInContext; +pub const Int64TypeInContext = c.LLVMInt64TypeInContext; +pub const Int8TypeInContext = c.LLVMInt8TypeInContext; +pub const IntPtrTypeForASInContext = c.LLVMIntPtrTypeForASInContext; +pub const IntPtrTypeInContext = c.LLVMIntPtrTypeInContext; +pub const IntTypeInContext = c.LLVMIntTypeInContext; +pub const LabelTypeInContext = c.LLVMLabelTypeInContext; +pub const MDNodeInContext = c.LLVMMDNodeInContext; +pub const MDStringInContext = c.LLVMMDStringInContext; +pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; +pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; +pub const StructTypeInContext = c.LLVMStructTypeInContext; +pub const TokenTypeInContext = c.LLVMTokenTypeInContext; pub const VoidTypeInContext = c.LLVMVoidTypeInContext; +pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; +pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; +pub const ConstAllOnes = c.LLVMConstAllOnes; +pub const ConstNull = c.LLVMConstNull; + +pub const VerifyModule = LLVMVerifyModule; +extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; + +pub const GetInsertBlock = LLVMGetInsertBlock; +extern fn LLVMGetInsertBlock(Builder: BuilderRef) BasicBlockRef; pub const FunctionType = LLVMFunctionType; extern fn LLVMFunctionType( ReturnType: TypeRef, ParamTypes: [*]TypeRef, ParamCount: c_uint, - IsVarArg: c_int, + IsVarArg: Bool, ) ?TypeRef; +pub const GetParam = LLVMGetParam; +extern fn LLVMGetParam(Fn: ValueRef, Index: c_uint) ValueRef; + +pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext; +extern fn LLVMAppendBasicBlockInContext(C: ContextRef, Fn: ValueRef, Name: [*]const u8) ?BasicBlockRef; + +pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd; +extern fn LLVMPositionBuilderAtEnd(Builder: BuilderRef, Block: BasicBlockRef) void; + +pub const AbortProcessAction = VerifierFailureAction.LLVMAbortProcessAction; +pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; +pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; +pub const VerifierFailureAction = c.LLVMVerifierFailureAction; + fn removeNullability(comptime T: type) type { comptime assert(@typeId(T) == builtin.TypeId.Optional); return T.Child; } + +pub const BuildRet = LLVMBuildRet; +extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 6fd6456b12..4326617fa0 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const Allocator = mem.Allocator; const Decl = @import("decl.zig").Decl; const Compilation = @import("compilation.zig").Compilation; @@ -6,6 +7,7 @@ const mem = std.mem; const ast = std.zig.ast; const Value = @import("value.zig").Value; const ir = @import("ir.zig"); +const Span = @import("errmsg.zig").Span; pub const Scope = struct { id: Id, @@ -93,6 +95,35 @@ pub const Scope = struct { end_block: *ir.BasicBlock, is_comptime: *ir.Instruction, + safety: Safety, + + const Safety = union(enum) { + Auto, + Manual: Manual, + + const Manual = struct { + /// the source span that disabled the safety value + span: Span, + + /// whether safety is enabled + enabled: bool, + }; + + fn get(self: Safety, comp: *Compilation) bool { + return switch (self) { + Safety.Auto => switch (comp.build_mode) { + builtin.Mode.Debug, + builtin.Mode.ReleaseSafe, + => true, + builtin.Mode.ReleaseFast, + builtin.Mode.ReleaseSmall, + => false, + }, + @TagType(Safety).Manual => |man| man.enabled, + }; + } + }; + /// Creates a Block scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { const self = try comp.a().create(Block{ @@ -105,6 +136,7 @@ pub const Scope = struct { .incoming_blocks = undefined, .end_block = undefined, .is_comptime = undefined, + .safety = Safety.Auto, }); errdefer comp.a().destroy(self); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 8349047749..670547cce2 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -72,6 +72,81 @@ pub const Type = struct { } } + pub fn handleIsPtr(base: *Type) bool { + switch (base.id) { + Id.Type, + Id.ComptimeFloat, + Id.ComptimeInt, + Id.Undefined, + Id.Null, + Id.Namespace, + Id.Block, + Id.BoundFn, + Id.ArgTuple, + Id.Opaque, + => unreachable, + + Id.NoReturn, + Id.Void, + Id.Bool, + Id.Int, + Id.Float, + Id.Pointer, + Id.ErrorSet, + Id.Enum, + Id.Fn, + Id.Promise, + => return false, + + Id.Struct => @panic("TODO"), + Id.Array => @panic("TODO"), + Id.Optional => @panic("TODO"), + Id.ErrorUnion => @panic("TODO"), + Id.Union => @panic("TODO"), + } + } + + pub fn hasBits(base: *Type) bool { + switch (base.id) { + Id.Type, + Id.ComptimeFloat, + Id.ComptimeInt, + Id.Undefined, + Id.Null, + Id.Namespace, + Id.Block, + Id.BoundFn, + Id.ArgTuple, + Id.Opaque, + => unreachable, + + Id.Void, + Id.NoReturn, + => return false, + + Id.Bool, + Id.Int, + Id.Float, + Id.Fn, + Id.Promise, + => return true, + + Id.ErrorSet => @panic("TODO"), + Id.Enum => @panic("TODO"), + Id.Pointer => @panic("TODO"), + Id.Struct => @panic("TODO"), + Id.Array => @panic("TODO"), + Id.Optional => @panic("TODO"), + Id.ErrorUnion => @panic("TODO"), + Id.Union => @panic("TODO"), + } + } + + pub fn cast(base: *Type, comptime T: type) ?*T { + if (base.id != @field(Id, @typeName(T))) return null; + return @fieldParentPtr(T, "base", base); + } + pub fn dump(base: *const Type) void { std.debug.warn("{}", @tagName(base.id)); } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 8c047b1513..e3b91d2807 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -2,6 +2,8 @@ const std = @import("std"); const builtin = @import("builtin"); const Scope = @import("scope.zig").Scope; const Compilation = @import("compilation.zig").Compilation; +const ObjectFile = @import("codegen.zig").ObjectFile; +const llvm = @import("llvm.zig"); /// Values are ref-counted, heap-allocated, and copy-on-write /// If there is only 1 ref then write need not copy @@ -39,6 +41,17 @@ pub const Value = struct { std.debug.warn("{}", @tagName(base.id)); } + pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) { + switch (base.id) { + Id.Type => unreachable, + Id.Fn => @panic("TODO"), + Id.Void => return null, + Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), + Id.NoReturn => unreachable, + Id.Ptr => @panic("TODO"), + } + } + pub const Id = enum { Type, Fn, @@ -123,6 +136,15 @@ pub const Value = struct { pub fn destroy(self: *Bool, comp: *Compilation) void { comp.a().destroy(self); } + + pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { + const llvm_type = llvm.Int1TypeInContext(ofile.context); + if (self.x) { + return llvm.ConstAllOnes(llvm_type); + } else { + return llvm.ConstNull(llvm_type); + } + } }; pub const NoReturn = struct { diff --git a/std/event/future.zig b/std/event/future.zig index 23fa570c8f..0f27b4131b 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -40,6 +40,16 @@ pub fn Future(comptime T: type) type { return &self.data; } + /// Gets the data without waiting for it. If it's available, a pointer is + /// returned. Otherwise, null is returned. + pub fn getOrNull(self: *Self) ?*T { + if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { + return &self.data; + } else { + return null; + } + } + /// Make the data become available. May be called only once. /// Before calling this, modify the `data` property. pub fn resolve(self: *Self) void {