| author | |
| committer | |
| log | 363f4facea7fac2d6cfeab9d1d276ecd8e8e4df0 |
| tree | 0700a52b948f7626a85a120903e9299fae38e5f0 |
| parent | 28c3d4809bc6d497ac81892bc7eb03b95d8c2b32 |
8 files changed, 412 insertions(+), 11 deletions(-)
src-self-hosted/codegen.zig+157-1| ... | @@ -8,6 +8,7 @@ const ir = @import("ir.zig"); | ... | @@ -8,6 +8,7 @@ const ir = @import("ir.zig"); |
| 8 | const Value = @import("value.zig").Value; | 8 | const Value = @import("value.zig").Value; |
| 9 | const Type = @import("type.zig").Type; | 9 | const Type = @import("type.zig").Type; |
| 10 | const event = std.event; | 10 | const event = std.event; |
| 11 | const assert = std.debug.assert; | ||
| 11 | 12 | ||
| 12 | pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { | 13 | pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { |
| 13 | fn_val.base.ref(); | 14 | fn_val.base.ref(); |
| ... | @@ -35,9 +36,23 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) | ... | @@ -35,9 +36,23 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) |
| 35 | 36 | ||
| 36 | try renderToLlvmModule(&ofile, fn_val, code); | 37 | try renderToLlvmModule(&ofile, fn_val, code); |
| 37 | 38 | ||
| 39 | // TODO module level assembly | ||
| 40 | //if (buf_len(&g->global_asm) != 0) { | ||
| 41 | // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); | ||
| 42 | //} | ||
| 43 | |||
| 44 | // TODO | ||
| 45 | //ZigLLVMDIBuilderFinalize(g->dbuilder); | ||
| 46 | |||
| 38 | if (comp.verbose_llvm_ir) { | 47 | if (comp.verbose_llvm_ir) { |
| 39 | llvm.DumpModule(ofile.module); | 48 | llvm.DumpModule(ofile.module); |
| 40 | } | 49 | } |
| 50 | |||
| 51 | // verify the llvm module when safety is on | ||
| 52 | if (std.debug.runtime_safety) { | ||
| 53 | var error_ptr: ?[*]u8 = null; | ||
| 54 | _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); | ||
| 55 | } | ||
| 41 | } | 56 | } |
| 42 | 57 | ||
| 43 | pub const ObjectFile = struct { | 58 | pub const ObjectFile = struct { |
| ... | @@ -55,5 +70,146 @@ pub const ObjectFile = struct { | ... | @@ -55,5 +70,146 @@ pub const ObjectFile = struct { |
| 55 | pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void { | 70 | pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void { |
| 56 | // TODO audit more of codegen.cpp:fn_llvm_value and port more logic | 71 | // TODO audit more of codegen.cpp:fn_llvm_value and port more logic |
| 57 | const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile); | 72 | const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile); |
| 58 | const llvm_fn = llvm.AddFunction(ofile.module, fn_val.symbol_name.ptr(), llvm_fn_type); | 73 | const llvm_fn = llvm.AddFunction( |
| 74 | ofile.module, | ||
| 75 | fn_val.symbol_name.ptr(), | ||
| 76 | llvm_fn_type, | ||
| 77 | ) orelse return error.OutOfMemory; | ||
| 78 | |||
| 79 | const want_fn_safety = fn_val.block_scope.safety.get(ofile.comp); | ||
| 80 | if (want_fn_safety and ofile.comp.haveLibC()) { | ||
| 81 | try addLLVMFnAttr(ofile, llvm_fn, "sspstrong"); | ||
| 82 | try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4"); | ||
| 83 | } | ||
| 84 | |||
| 85 | // TODO | ||
| 86 | //if (fn_val.align_stack) |align_stack| { | ||
| 87 | // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack); | ||
| 88 | //} | ||
| 89 | |||
| 90 | const fn_type = fn_val.base.typeof.cast(Type.Fn).?; | ||
| 91 | |||
| 92 | try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); | ||
| 93 | //add_uwtable_attr(g, fn_table_entry->llvm_value); | ||
| 94 | try addLLVMFnAttr(ofile, llvm_fn, "nobuiltin"); | ||
| 95 | |||
| 96 | //if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) { | ||
| 97 | // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim", "true"); | ||
| 98 | // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim-non-leaf", nullptr); | ||
| 99 | //} | ||
| 100 | |||
| 101 | //if (fn_table_entry->section_name) { | ||
| 102 | // LLVMSetSection(fn_table_entry->llvm_value, buf_ptr(fn_table_entry->section_name)); | ||
| 103 | //} | ||
| 104 | //if (fn_table_entry->align_bytes > 0) { | ||
| 105 | // LLVMSetAlignment(fn_table_entry->llvm_value, (unsigned)fn_table_entry->align_bytes); | ||
| 106 | //} else { | ||
| 107 | // // We'd like to set the best alignment for the function here, but on Darwin LLVM gives | ||
| 108 | // // "Cannot getTypeInfo() on a type that is unsized!" assertion failure when calling | ||
| 109 | // // any of the functions for getting alignment. Not specifying the alignment should | ||
| 110 | // // use the ABI alignment, which is fine. | ||
| 111 | //} | ||
| 112 | |||
| 113 | //if (!type_has_bits(return_type)) { | ||
| 114 | // // nothing to do | ||
| 115 | //} else if (type_is_codegen_pointer(return_type)) { | ||
| 116 | // addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); | ||
| 117 | //} else if (handle_is_ptr(return_type) && | ||
| 118 | // calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc)) | ||
| 119 | //{ | ||
| 120 | // addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); | ||
| 121 | // addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); | ||
| 122 | //} | ||
| 123 | |||
| 124 | // TODO set parameter attributes | ||
| 125 | |||
| 126 | // TODO | ||
| 127 | //uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); | ||
| 128 | //if (err_ret_trace_arg_index != UINT32_MAX) { | ||
| 129 | // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); | ||
| 130 | //} | ||
| 131 | |||
| 132 | const cur_ret_ptr = if (fn_type.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null; | ||
| 133 | |||
| 134 | // build all basic blocks | ||
| 135 | for (code.basic_block_list.toSlice()) |bb| { | ||
| 136 | bb.llvm_block = llvm.AppendBasicBlockInContext( | ||
| 137 | ofile.context, | ||
| 138 | llvm_fn, | ||
| 139 | bb.name_hint, | ||
| 140 | ) orelse return error.OutOfMemory; | ||
| 141 | } | ||
| 142 | const entry_bb = code.basic_block_list.at(0); | ||
| 143 | llvm.PositionBuilderAtEnd(ofile.builder, entry_bb.llvm_block); | ||
| 144 | |||
| 145 | llvm.ClearCurrentDebugLocation(ofile.builder); | ||
| 146 | |||
| 147 | // TODO set up error return tracing | ||
| 148 | // TODO allocate temporary stack values | ||
| 149 | // TODO create debug variable declarations for variables and allocate all local variables | ||
| 150 | // TODO finishing error return trace setup. we have to do this after all the allocas. | ||
| 151 | // TODO create debug variable declarations for parameters | ||
| 152 | |||
| 153 | for (code.basic_block_list.toSlice()) |current_block| { | ||
| 154 | llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block); | ||
| 155 | for (current_block.instruction_list.toSlice()) |instruction| { | ||
| 156 | if (instruction.ref_count == 0 and !instruction.hasSideEffects()) continue; | ||
| 157 | |||
| 158 | instruction.llvm_value = try instruction.render(ofile, fn_val); | ||
| 159 | } | ||
| 160 | current_block.llvm_exit_block = llvm.GetInsertBlock(ofile.builder); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | fn addLLVMAttr( | ||
| 165 | ofile: *ObjectFile, | ||
| 166 | val: llvm.ValueRef, | ||
| 167 | attr_index: llvm.AttributeIndex, | ||
| 168 | attr_name: []const u8, | ||
| 169 | ) !void { | ||
| 170 | const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len); | ||
| 171 | assert(kind_id != 0); | ||
| 172 | const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, 0) orelse return error.OutOfMemory; | ||
| 173 | llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); | ||
| 174 | } | ||
| 175 | |||
| 176 | fn addLLVMAttrStr( | ||
| 177 | ofile: *ObjectFile, | ||
| 178 | val: llvm.ValueRef, | ||
| 179 | attr_index: llvm.AttributeIndex, | ||
| 180 | attr_name: []const u8, | ||
| 181 | attr_val: []const u8, | ||
| 182 | ) !void { | ||
| 183 | const llvm_attr = llvm.CreateStringAttribute( | ||
| 184 | ofile.context, | ||
| 185 | attr_name.ptr, | ||
| 186 | @intCast(c_uint, attr_name.len), | ||
| 187 | attr_val.ptr, | ||
| 188 | @intCast(c_uint, attr_val.len), | ||
| 189 | ) orelse return error.OutOfMemory; | ||
| 190 | llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); | ||
| 191 | } | ||
| 192 | |||
| 193 | fn addLLVMAttrInt( | ||
| 194 | val: llvm.ValueRef, | ||
| 195 | attr_index: llvm.AttributeIndex, | ||
| 196 | attr_name: []const u8, | ||
| 197 | attr_val: u64, | ||
| 198 | ) !void { | ||
| 199 | const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len); | ||
| 200 | assert(kind_id != 0); | ||
| 201 | const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, attr_val) orelse return error.OutOfMemory; | ||
| 202 | llvm.AddAttributeAtIndex(val, attr_index, llvm_attr); | ||
| 203 | } | ||
| 204 | |||
| 205 | fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8) !void { | ||
| 206 | return addLLVMAttr(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name); | ||
| 207 | } | ||
| 208 | |||
| 209 | fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: []const u8) !void { | ||
| 210 | return addLLVMAttrStr(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val); | ||
| 211 | } | ||
| 212 | |||
| 213 | fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void { | ||
| 214 | return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val); | ||
| 59 | } | 215 | } |
src-self-hosted/compilation.zig+5-1| ... | @@ -606,6 +606,10 @@ pub const Compilation = struct { | ... | @@ -606,6 +606,10 @@ pub const Compilation = struct { |
| 606 | return error.Todo; | 606 | return error.Todo; |
| 607 | } | 607 | } |
| 608 | 608 | ||
| 609 | pub fn haveLibC(self: *Compilation) bool { | ||
| 610 | return self.libc_link_lib != null; | ||
| 611 | } | ||
| 612 | |||
| 609 | pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib { | 613 | pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib { |
| 610 | const is_libc = mem.eql(u8, name, "c"); | 614 | const is_libc = mem.eql(u8, name, "c"); |
| 611 | 615 | ||
| ... | @@ -741,7 +745,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { | ... | @@ -741,7 +745,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { |
| 741 | analyzed_code.dump(); | 745 | analyzed_code.dump(); |
| 742 | } | 746 | } |
| 743 | 747 | ||
| 744 | // Kick off rendering to LLVM comp, but it doesn't block the fn decl | 748 | // Kick off rendering to LLVM module, but it doesn't block the fn decl |
| 745 | // analysis from being complete. | 749 | // analysis from being complete. |
| 746 | try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); | 750 | try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); |
| 747 | } | 751 | } |
src-self-hosted/ir.zig+48-8| ... | @@ -10,6 +10,8 @@ const assert = std.debug.assert; | ... | @@ -10,6 +10,8 @@ const assert = std.debug.assert; |
| 10 | const Token = std.zig.Token; | 10 | const Token = std.zig.Token; |
| 11 | const ParsedFile = @import("parsed_file.zig").ParsedFile; | 11 | const ParsedFile = @import("parsed_file.zig").ParsedFile; |
| 12 | const Span = @import("errmsg.zig").Span; | 12 | const Span = @import("errmsg.zig").Span; |
| 13 | const llvm = @import("llvm.zig"); | ||
| 14 | const ObjectFile = @import("codegen.zig").ObjectFile; | ||
| 13 | 15 | ||
| 14 | pub const LVal = enum { | 16 | pub const LVal = enum { |
| 15 | None, | 17 | None, |
| ... | @@ -61,6 +63,9 @@ pub const Instruction = struct { | ... | @@ -61,6 +63,9 @@ pub const Instruction = struct { |
| 61 | /// the instruction that this one derives from in analysis | 63 | /// the instruction that this one derives from in analysis |
| 62 | parent: ?*Instruction, | 64 | parent: ?*Instruction, |
| 63 | 65 | ||
| 66 | /// populated durign codegen | ||
| 67 | llvm_value: ?llvm.ValueRef, | ||
| 68 | |||
| 64 | pub fn cast(base: *Instruction, comptime T: type) ?*T { | 69 | pub fn cast(base: *Instruction, comptime T: type) ?*T { |
| 65 | if (base.id == comptime typeToId(T)) { | 70 | if (base.id == comptime typeToId(T)) { |
| 66 | return @fieldParentPtr(T, "base", base); | 71 | return @fieldParentPtr(T, "base", base); |
| ... | @@ -108,14 +113,25 @@ pub const Instruction = struct { | ... | @@ -108,14 +113,25 @@ pub const Instruction = struct { |
| 108 | inline while (i < @memberCount(Id)) : (i += 1) { | 113 | inline while (i < @memberCount(Id)) : (i += 1) { |
| 109 | if (base.id == @field(Id, @memberName(Id, i))) { | 114 | if (base.id == @field(Id, @memberName(Id, i))) { |
| 110 | const T = @field(Instruction, @memberName(Id, i)); | 115 | const T = @field(Instruction, @memberName(Id, i)); |
| 111 | const new_inst = try @fieldParentPtr(T, "base", base).analyze(ira); | 116 | return @fieldParentPtr(T, "base", base).analyze(ira); |
| 112 | new_inst.linkToParent(base); | ||
| 113 | return new_inst; | ||
| 114 | } | 117 | } |
| 115 | } | 118 | } |
| 116 | unreachable; | 119 | unreachable; |
| 117 | } | 120 | } |
| 118 | 121 | ||
| 122 | pub fn render(base: *Instruction, ofile: *ObjectFile, fn_val: *Value.Fn) (error{OutOfMemory}!?llvm.ValueRef) { | ||
| 123 | switch (base.id) { | ||
| 124 | Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), | ||
| 125 | Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), | ||
| 126 | Id.Ref => @panic("TODO"), | ||
| 127 | Id.DeclVar => @panic("TODO"), | ||
| 128 | Id.CheckVoidStmt => @panic("TODO"), | ||
| 129 | Id.Phi => @panic("TODO"), | ||
| 130 | Id.Br => @panic("TODO"), | ||
| 131 | Id.AddImplicitReturnType => @panic("TODO"), | ||
| 132 | } | ||
| 133 | } | ||
| 134 | |||
| 119 | fn getAsParam(param: *Instruction) !*Instruction { | 135 | fn getAsParam(param: *Instruction) !*Instruction { |
| 120 | const child = param.child orelse return error.SemanticAnalysisFailed; | 136 | const child = param.child orelse return error.SemanticAnalysisFailed; |
| 121 | switch (child.val) { | 137 | switch (child.val) { |
| ... | @@ -186,6 +202,10 @@ pub const Instruction = struct { | ... | @@ -186,6 +202,10 @@ pub const Instruction = struct { |
| 186 | new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() }; | 202 | new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() }; |
| 187 | return new_inst; | 203 | return new_inst; |
| 188 | } | 204 | } |
| 205 | |||
| 206 | pub fn render(self: *Const, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { | ||
| 207 | return self.base.val.KnownValue.getLlvmConst(ofile); | ||
| 208 | } | ||
| 189 | }; | 209 | }; |
| 190 | 210 | ||
| 191 | pub const Return = struct { | 211 | pub const Return = struct { |
| ... | @@ -214,6 +234,18 @@ pub const Instruction = struct { | ... | @@ -214,6 +234,18 @@ pub const Instruction = struct { |
| 214 | 234 | ||
| 215 | return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value }); | 235 | return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value }); |
| 216 | } | 236 | } |
| 237 | |||
| 238 | pub fn render(self: *Return, ofile: *ObjectFile, fn_val: *Value.Fn) ?llvm.ValueRef { | ||
| 239 | const value = self.params.return_value.llvm_value; | ||
| 240 | const return_type = self.params.return_value.getKnownType(); | ||
| 241 | |||
| 242 | if (return_type.handleIsPtr()) { | ||
| 243 | @panic("TODO"); | ||
| 244 | } else { | ||
| 245 | _ = llvm.BuildRet(ofile.builder, value); | ||
| 246 | } | ||
| 247 | return null; | ||
| 248 | } | ||
| 217 | }; | 249 | }; |
| 218 | 250 | ||
| 219 | pub const Ref = struct { | 251 | pub const Ref = struct { |
| ... | @@ -387,12 +419,16 @@ pub const Variable = struct { | ... | @@ -387,12 +419,16 @@ pub const Variable = struct { |
| 387 | 419 | ||
| 388 | pub const BasicBlock = struct { | 420 | pub const BasicBlock = struct { |
| 389 | ref_count: usize, | 421 | ref_count: usize, |
| 390 | name_hint: []const u8, | 422 | name_hint: [*]const u8, // must be a C string literal |
| 391 | debug_id: usize, | 423 | debug_id: usize, |
| 392 | scope: *Scope, | 424 | scope: *Scope, |
| 393 | instruction_list: std.ArrayList(*Instruction), | 425 | instruction_list: std.ArrayList(*Instruction), |
| 394 | ref_instruction: ?*Instruction, | 426 | ref_instruction: ?*Instruction, |
| 395 | 427 | ||
| 428 | /// for codegen | ||
| 429 | llvm_block: llvm.BasicBlockRef, | ||
| 430 | llvm_exit_block: llvm.BasicBlockRef, | ||
| 431 | |||
| 396 | /// the basic block that is derived from this one in analysis | 432 | /// the basic block that is derived from this one in analysis |
| 397 | child: ?*BasicBlock, | 433 | child: ?*BasicBlock, |
| 398 | 434 | ||
| ... | @@ -426,7 +462,7 @@ pub const Code = struct { | ... | @@ -426,7 +462,7 @@ pub const Code = struct { |
| 426 | pub fn dump(self: *Code) void { | 462 | pub fn dump(self: *Code) void { |
| 427 | var bb_i: usize = 0; | 463 | var bb_i: usize = 0; |
| 428 | for (self.basic_block_list.toSliceConst()) |bb| { | 464 | for (self.basic_block_list.toSliceConst()) |bb| { |
| 429 | std.debug.warn("{}_{}:\n", bb.name_hint, bb.debug_id); | 465 | std.debug.warn("{s}_{}:\n", bb.name_hint, bb.debug_id); |
| 430 | for (bb.instruction_list.toSliceConst()) |instr| { | 466 | for (bb.instruction_list.toSliceConst()) |instr| { |
| 431 | std.debug.warn(" "); | 467 | std.debug.warn(" "); |
| 432 | instr.dump(); | 468 | instr.dump(); |
| ... | @@ -475,7 +511,7 @@ pub const Builder = struct { | ... | @@ -475,7 +511,7 @@ pub const Builder = struct { |
| 475 | } | 511 | } |
| 476 | 512 | ||
| 477 | /// No need to clean up resources thanks to the arena allocator. | 513 | /// No need to clean up resources thanks to the arena allocator. |
| 478 | pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: []const u8) !*BasicBlock { | 514 | pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock { |
| 479 | const basic_block = try self.arena().create(BasicBlock{ | 515 | const basic_block = try self.arena().create(BasicBlock{ |
| 480 | .ref_count = 0, | 516 | .ref_count = 0, |
| 481 | .name_hint = name_hint, | 517 | .name_hint = name_hint, |
| ... | @@ -485,6 +521,8 @@ pub const Builder = struct { | ... | @@ -485,6 +521,8 @@ pub const Builder = struct { |
| 485 | .child = null, | 521 | .child = null, |
| 486 | .parent = null, | 522 | .parent = null, |
| 487 | .ref_instruction = null, | 523 | .ref_instruction = null, |
| 524 | .llvm_block = undefined, | ||
| 525 | .llvm_exit_block = undefined, | ||
| 488 | }); | 526 | }); |
| 489 | self.next_debug_id += 1; | 527 | self.next_debug_id += 1; |
| 490 | return basic_block; | 528 | return basic_block; |
| ... | @@ -600,7 +638,7 @@ pub const Builder = struct { | ... | @@ -600,7 +638,7 @@ pub const Builder = struct { |
| 600 | if (block.label) |label| { | 638 | if (block.label) |label| { |
| 601 | block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena()); | 639 | block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena()); |
| 602 | block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); | 640 | block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena()); |
| 603 | block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd"); | 641 | block_scope.end_block = try irb.createBasicBlock(parent_scope, c"BlockEnd"); |
| 604 | block_scope.is_comptime = try irb.buildConstBool( | 642 | block_scope.is_comptime = try irb.buildConstBool( |
| 605 | parent_scope, | 643 | parent_scope, |
| 606 | Span.token(block.lbrace), | 644 | Span.token(block.lbrace), |
| ... | @@ -777,6 +815,7 @@ pub const Builder = struct { | ... | @@ -777,6 +815,7 @@ pub const Builder = struct { |
| 777 | .span = span, | 815 | .span = span, |
| 778 | .child = null, | 816 | .child = null, |
| 779 | .parent = null, | 817 | .parent = null, |
| 818 | .llvm_value = undefined, | ||
| 780 | }, | 819 | }, |
| 781 | .params = params, | 820 | .params = params, |
| 782 | }); | 821 | }); |
| ... | @@ -968,7 +1007,7 @@ pub async fn gen( | ... | @@ -968,7 +1007,7 @@ pub async fn gen( |
| 968 | var irb = try Builder.init(comp, parsed_file); | 1007 | var irb = try Builder.init(comp, parsed_file); |
| 969 | errdefer irb.abort(); | 1008 | errdefer irb.abort(); |
| 970 | 1009 | ||
| 971 | const entry_block = try irb.createBasicBlock(scope, "Entry"); | 1010 | const entry_block = try irb.createBasicBlock(scope, c"Entry"); |
| 972 | entry_block.ref(); // Entry block gets a reference because we enter it to begin. | 1011 | entry_block.ref(); // Entry block gets a reference because we enter it to begin. |
| 973 | try irb.setCursorAtEndAndAppendBlock(entry_block); | 1012 | try irb.setCursorAtEndAndAppendBlock(entry_block); |
| 974 | 1013 | ||
| ... | @@ -1013,6 +1052,7 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co | ... | @@ -1013,6 +1052,7 @@ pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Co |
| 1013 | } | 1052 | } |
| 1014 | 1053 | ||
| 1015 | const return_inst = try old_instruction.analyze(&ira); | 1054 | const return_inst = try old_instruction.analyze(&ira); |
| 1055 | return_inst.linkToParent(old_instruction); | ||
| 1016 | // Note: if we ever modify the above to handle error.CompileError by continuing analysis, | 1056 | // Note: if we ever modify the above to handle error.CompileError by continuing analysis, |
| 1017 | // then here we want to check if ira.isCompTime() and return early if true | 1057 | // then here we want to check if ira.isCompTime() and return early if true |
| 1018 | 1058 |
src-self-hosted/llvm.zig+63-1| ... | @@ -2,29 +2,91 @@ const builtin = @import("builtin"); | ... | @@ -2,29 +2,91 @@ const builtin = @import("builtin"); |
| 2 | const c = @import("c.zig"); | 2 | const c = @import("c.zig"); |
| 3 | const assert = @import("std").debug.assert; | 3 | const assert = @import("std").debug.assert; |
| 4 | 4 | ||
| 5 | pub const AttributeIndex = c_uint; | ||
| 6 | pub const Bool = c_int; | ||
| 7 | |||
| 5 | pub const BuilderRef = removeNullability(c.LLVMBuilderRef); | 8 | pub const BuilderRef = removeNullability(c.LLVMBuilderRef); |
| 6 | pub const ContextRef = removeNullability(c.LLVMContextRef); | 9 | pub const ContextRef = removeNullability(c.LLVMContextRef); |
| 7 | pub const ModuleRef = removeNullability(c.LLVMModuleRef); | 10 | pub const ModuleRef = removeNullability(c.LLVMModuleRef); |
| 8 | pub const ValueRef = removeNullability(c.LLVMValueRef); | 11 | pub const ValueRef = removeNullability(c.LLVMValueRef); |
| 9 | pub const TypeRef = removeNullability(c.LLVMTypeRef); | 12 | pub const TypeRef = removeNullability(c.LLVMTypeRef); |
| 13 | pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); | ||
| 14 | pub const AttributeRef = removeNullability(c.LLVMAttributeRef); | ||
| 10 | 15 | ||
| 16 | pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; | ||
| 11 | pub const AddFunction = c.LLVMAddFunction; | 17 | pub const AddFunction = c.LLVMAddFunction; |
| 18 | pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; | ||
| 19 | pub const ConstInt = c.LLVMConstInt; | ||
| 20 | pub const ConstStringInContext = c.LLVMConstStringInContext; | ||
| 21 | pub const ConstStructInContext = c.LLVMConstStructInContext; | ||
| 12 | pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; | 22 | pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; |
| 23 | pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; | ||
| 24 | pub const CreateStringAttribute = c.LLVMCreateStringAttribute; | ||
| 13 | pub const DisposeBuilder = c.LLVMDisposeBuilder; | 25 | pub const DisposeBuilder = c.LLVMDisposeBuilder; |
| 14 | pub const DisposeModule = c.LLVMDisposeModule; | 26 | pub const DisposeModule = c.LLVMDisposeModule; |
| 27 | pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; | ||
| 15 | pub const DumpModule = c.LLVMDumpModule; | 28 | pub const DumpModule = c.LLVMDumpModule; |
| 29 | pub const FP128TypeInContext = c.LLVMFP128TypeInContext; | ||
| 30 | pub const FloatTypeInContext = c.LLVMFloatTypeInContext; | ||
| 31 | pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; | ||
| 32 | pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; | ||
| 33 | pub const HalfTypeInContext = c.LLVMHalfTypeInContext; | ||
| 34 | pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; | ||
| 35 | pub const Int128TypeInContext = c.LLVMInt128TypeInContext; | ||
| 36 | pub const Int16TypeInContext = c.LLVMInt16TypeInContext; | ||
| 37 | pub const Int1TypeInContext = c.LLVMInt1TypeInContext; | ||
| 38 | pub const Int32TypeInContext = c.LLVMInt32TypeInContext; | ||
| 39 | pub const Int64TypeInContext = c.LLVMInt64TypeInContext; | ||
| 40 | pub const Int8TypeInContext = c.LLVMInt8TypeInContext; | ||
| 41 | pub const IntPtrTypeForASInContext = c.LLVMIntPtrTypeForASInContext; | ||
| 42 | pub const IntPtrTypeInContext = c.LLVMIntPtrTypeInContext; | ||
| 43 | pub const IntTypeInContext = c.LLVMIntTypeInContext; | ||
| 44 | pub const LabelTypeInContext = c.LLVMLabelTypeInContext; | ||
| 45 | pub const MDNodeInContext = c.LLVMMDNodeInContext; | ||
| 46 | pub const MDStringInContext = c.LLVMMDStringInContext; | ||
| 47 | pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; | ||
| 16 | pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; | 48 | pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; |
| 49 | pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; | ||
| 50 | pub const StructTypeInContext = c.LLVMStructTypeInContext; | ||
| 51 | pub const TokenTypeInContext = c.LLVMTokenTypeInContext; | ||
| 17 | pub const VoidTypeInContext = c.LLVMVoidTypeInContext; | 52 | pub const VoidTypeInContext = c.LLVMVoidTypeInContext; |
| 53 | pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; | ||
| 54 | pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; | ||
| 55 | pub const ConstAllOnes = c.LLVMConstAllOnes; | ||
| 56 | pub const ConstNull = c.LLVMConstNull; | ||
| 57 | |||
| 58 | pub const VerifyModule = LLVMVerifyModule; | ||
| 59 | extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; | ||
| 60 | |||
| 61 | pub const GetInsertBlock = LLVMGetInsertBlock; | ||
| 62 | extern fn LLVMGetInsertBlock(Builder: BuilderRef) BasicBlockRef; | ||
| 18 | 63 | ||
| 19 | pub const FunctionType = LLVMFunctionType; | 64 | pub const FunctionType = LLVMFunctionType; |
| 20 | extern fn LLVMFunctionType( | 65 | extern fn LLVMFunctionType( |
| 21 | ReturnType: TypeRef, | 66 | ReturnType: TypeRef, |
| 22 | ParamTypes: [*]TypeRef, | 67 | ParamTypes: [*]TypeRef, |
| 23 | ParamCount: c_uint, | 68 | ParamCount: c_uint, |
| 24 | IsVarArg: c_int, | 69 | IsVarArg: Bool, |
| 25 | ) ?TypeRef; | 70 | ) ?TypeRef; |
| 26 | 71 | ||
| 72 | pub const GetParam = LLVMGetParam; | ||
| 73 | extern fn LLVMGetParam(Fn: ValueRef, Index: c_uint) ValueRef; | ||
| 74 | |||
| 75 | pub const AppendBasicBlockInContext = LLVMAppendBasicBlockInContext; | ||
| 76 | extern fn LLVMAppendBasicBlockInContext(C: ContextRef, Fn: ValueRef, Name: [*]const u8) ?BasicBlockRef; | ||
| 77 | |||
| 78 | pub const PositionBuilderAtEnd = LLVMPositionBuilderAtEnd; | ||
| 79 | extern fn LLVMPositionBuilderAtEnd(Builder: BuilderRef, Block: BasicBlockRef) void; | ||
| 80 | |||
| 81 | pub const AbortProcessAction = VerifierFailureAction.LLVMAbortProcessAction; | ||
| 82 | pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; | ||
| 83 | pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; | ||
| 84 | pub const VerifierFailureAction = c.LLVMVerifierFailureAction; | ||
| 85 | |||
| 27 | fn removeNullability(comptime T: type) type { | 86 | fn removeNullability(comptime T: type) type { |
| 28 | comptime assert(@typeId(T) == builtin.TypeId.Optional); | 87 | comptime assert(@typeId(T) == builtin.TypeId.Optional); |
| 29 | return T.Child; | 88 | return T.Child; |
| 30 | } | 89 | } |
| 90 | |||
| 91 | pub const BuildRet = LLVMBuildRet; | ||
| 92 | extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef; |
src-self-hosted/scope.zig+32| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const Allocator = mem.Allocator; | 3 | const Allocator = mem.Allocator; |
| 3 | const Decl = @import("decl.zig").Decl; | 4 | const Decl = @import("decl.zig").Decl; |
| 4 | const Compilation = @import("compilation.zig").Compilation; | 5 | const Compilation = @import("compilation.zig").Compilation; |
| ... | @@ -6,6 +7,7 @@ const mem = std.mem; | ... | @@ -6,6 +7,7 @@ const mem = std.mem; |
| 6 | const ast = std.zig.ast; | 7 | const ast = std.zig.ast; |
| 7 | const Value = @import("value.zig").Value; | 8 | const Value = @import("value.zig").Value; |
| 8 | const ir = @import("ir.zig"); | 9 | const ir = @import("ir.zig"); |
| 10 | const Span = @import("errmsg.zig").Span; | ||
| 9 | 11 | ||
| 10 | pub const Scope = struct { | 12 | pub const Scope = struct { |
| 11 | id: Id, | 13 | id: Id, |
| ... | @@ -93,6 +95,35 @@ pub const Scope = struct { | ... | @@ -93,6 +95,35 @@ pub const Scope = struct { |
| 93 | end_block: *ir.BasicBlock, | 95 | end_block: *ir.BasicBlock, |
| 94 | is_comptime: *ir.Instruction, | 96 | is_comptime: *ir.Instruction, |
| 95 | 97 | ||
| 98 | safety: Safety, | ||
| 99 | |||
| 100 | const Safety = union(enum) { | ||
| 101 | Auto, | ||
| 102 | Manual: Manual, | ||
| 103 | |||
| 104 | const Manual = struct { | ||
| 105 | /// the source span that disabled the safety value | ||
| 106 | span: Span, | ||
| 107 | |||
| 108 | /// whether safety is enabled | ||
| 109 | enabled: bool, | ||
| 110 | }; | ||
| 111 | |||
| 112 | fn get(self: Safety, comp: *Compilation) bool { | ||
| 113 | return switch (self) { | ||
| 114 | Safety.Auto => switch (comp.build_mode) { | ||
| 115 | builtin.Mode.Debug, | ||
| 116 | builtin.Mode.ReleaseSafe, | ||
| 117 | => true, | ||
| 118 | builtin.Mode.ReleaseFast, | ||
| 119 | builtin.Mode.ReleaseSmall, | ||
| 120 | => false, | ||
| 121 | }, | ||
| 122 | @TagType(Safety).Manual => |man| man.enabled, | ||
| 123 | }; | ||
| 124 | } | ||
| 125 | }; | ||
| 126 | |||
| 96 | /// Creates a Block scope with 1 reference | 127 | /// Creates a Block scope with 1 reference |
| 97 | pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { | 128 | pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { |
| 98 | const self = try comp.a().create(Block{ | 129 | const self = try comp.a().create(Block{ |
| ... | @@ -105,6 +136,7 @@ pub const Scope = struct { | ... | @@ -105,6 +136,7 @@ pub const Scope = struct { |
| 105 | .incoming_blocks = undefined, | 136 | .incoming_blocks = undefined, |
| 106 | .end_block = undefined, | 137 | .end_block = undefined, |
| 107 | .is_comptime = undefined, | 138 | .is_comptime = undefined, |
| 139 | .safety = Safety.Auto, | ||
| 108 | }); | 140 | }); |
| 109 | errdefer comp.a().destroy(self); | 141 | errdefer comp.a().destroy(self); |
| 110 | 142 |
src-self-hosted/type.zig+75| ... | @@ -72,6 +72,81 @@ pub const Type = struct { | ... | @@ -72,6 +72,81 @@ pub const Type = struct { |
| 72 | } | 72 | } |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | pub fn handleIsPtr(base: *Type) bool { | ||
| 76 | switch (base.id) { | ||
| 77 | Id.Type, | ||
| 78 | Id.ComptimeFloat, | ||
| 79 | Id.ComptimeInt, | ||
| 80 | Id.Undefined, | ||
| 81 | Id.Null, | ||
| 82 | Id.Namespace, | ||
| 83 | Id.Block, | ||
| 84 | Id.BoundFn, | ||
| 85 | Id.ArgTuple, | ||
| 86 | Id.Opaque, | ||
| 87 | => unreachable, | ||
| 88 | |||
| 89 | Id.NoReturn, | ||
| 90 | Id.Void, | ||
| 91 | Id.Bool, | ||
| 92 | Id.Int, | ||
| 93 | Id.Float, | ||
| 94 | Id.Pointer, | ||
| 95 | Id.ErrorSet, | ||
| 96 | Id.Enum, | ||
| 97 | Id.Fn, | ||
| 98 | Id.Promise, | ||
| 99 | => return false, | ||
| 100 | |||
| 101 | Id.Struct => @panic("TODO"), | ||
| 102 | Id.Array => @panic("TODO"), | ||
| 103 | Id.Optional => @panic("TODO"), | ||
| 104 | Id.ErrorUnion => @panic("TODO"), | ||
| 105 | Id.Union => @panic("TODO"), | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | pub fn hasBits(base: *Type) bool { | ||
| 110 | switch (base.id) { | ||
| 111 | Id.Type, | ||
| 112 | Id.ComptimeFloat, | ||
| 113 | Id.ComptimeInt, | ||
| 114 | Id.Undefined, | ||
| 115 | Id.Null, | ||
| 116 | Id.Namespace, | ||
| 117 | Id.Block, | ||
| 118 | Id.BoundFn, | ||
| 119 | Id.ArgTuple, | ||
| 120 | Id.Opaque, | ||
| 121 | => unreachable, | ||
| 122 | |||
| 123 | Id.Void, | ||
| 124 | Id.NoReturn, | ||
| 125 | => return false, | ||
| 126 | |||
| 127 | Id.Bool, | ||
| 128 | Id.Int, | ||
| 129 | Id.Float, | ||
| 130 | Id.Fn, | ||
| 131 | Id.Promise, | ||
| 132 | => return true, | ||
| 133 | |||
| 134 | Id.ErrorSet => @panic("TODO"), | ||
| 135 | Id.Enum => @panic("TODO"), | ||
| 136 | Id.Pointer => @panic("TODO"), | ||
| 137 | Id.Struct => @panic("TODO"), | ||
| 138 | Id.Array => @panic("TODO"), | ||
| 139 | Id.Optional => @panic("TODO"), | ||
| 140 | Id.ErrorUnion => @panic("TODO"), | ||
| 141 | Id.Union => @panic("TODO"), | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | pub fn cast(base: *Type, comptime T: type) ?*T { | ||
| 146 | if (base.id != @field(Id, @typeName(T))) return null; | ||
| 147 | return @fieldParentPtr(T, "base", base); | ||
| 148 | } | ||
| 149 | |||
| 75 | pub fn dump(base: *const Type) void { | 150 | pub fn dump(base: *const Type) void { |
| 76 | std.debug.warn("{}", @tagName(base.id)); | 151 | std.debug.warn("{}", @tagName(base.id)); |
| 77 | } | 152 | } |
src-self-hosted/value.zig+22| ... | @@ -2,6 +2,8 @@ const std = @import("std"); | ... | @@ -2,6 +2,8 @@ const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Scope = @import("scope.zig").Scope; | 3 | const Scope = @import("scope.zig").Scope; |
| 4 | const Compilation = @import("compilation.zig").Compilation; | 4 | const Compilation = @import("compilation.zig").Compilation; |
| 5 | const ObjectFile = @import("codegen.zig").ObjectFile; | ||
| 6 | const llvm = @import("llvm.zig"); | ||
| 5 | 7 | ||
| 6 | /// Values are ref-counted, heap-allocated, and copy-on-write | 8 | /// Values are ref-counted, heap-allocated, and copy-on-write |
| 7 | /// If there is only 1 ref then write need not copy | 9 | /// If there is only 1 ref then write need not copy |
| ... | @@ -39,6 +41,17 @@ pub const Value = struct { | ... | @@ -39,6 +41,17 @@ pub const Value = struct { |
| 39 | std.debug.warn("{}", @tagName(base.id)); | 41 | std.debug.warn("{}", @tagName(base.id)); |
| 40 | } | 42 | } |
| 41 | 43 | ||
| 44 | pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) { | ||
| 45 | switch (base.id) { | ||
| 46 | Id.Type => unreachable, | ||
| 47 | Id.Fn => @panic("TODO"), | ||
| 48 | Id.Void => return null, | ||
| 49 | Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), | ||
| 50 | Id.NoReturn => unreachable, | ||
| 51 | Id.Ptr => @panic("TODO"), | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 42 | pub const Id = enum { | 55 | pub const Id = enum { |
| 43 | Type, | 56 | Type, |
| 44 | Fn, | 57 | Fn, |
| ... | @@ -123,6 +136,15 @@ pub const Value = struct { | ... | @@ -123,6 +136,15 @@ pub const Value = struct { |
| 123 | pub fn destroy(self: *Bool, comp: *Compilation) void { | 136 | pub fn destroy(self: *Bool, comp: *Compilation) void { |
| 124 | comp.a().destroy(self); | 137 | comp.a().destroy(self); |
| 125 | } | 138 | } |
| 139 | |||
| 140 | pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { | ||
| 141 | const llvm_type = llvm.Int1TypeInContext(ofile.context); | ||
| 142 | if (self.x) { | ||
| 143 | return llvm.ConstAllOnes(llvm_type); | ||
| 144 | } else { | ||
| 145 | return llvm.ConstNull(llvm_type); | ||
| 146 | } | ||
| 147 | } | ||
| 126 | }; | 148 | }; |
| 127 | 149 | ||
| 128 | pub const NoReturn = struct { | 150 | pub const NoReturn = struct { |
std/event/future.zig+10| ... | @@ -40,6 +40,16 @@ pub fn Future(comptime T: type) type { | ... | @@ -40,6 +40,16 @@ pub fn Future(comptime T: type) type { |
| 40 | return &self.data; | 40 | return &self.data; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | /// Gets the data without waiting for it. If it's available, a pointer is | ||
| 44 | /// returned. Otherwise, null is returned. | ||
| 45 | pub fn getOrNull(self: *Self) ?*T { | ||
| 46 | if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { | ||
| 47 | return &self.data; | ||
| 48 | } else { | ||
| 49 | return null; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 43 | /// Make the data become available. May be called only once. | 53 | /// Make the data become available. May be called only once. |
| 44 | /// Before calling this, modify the `data` property. | 54 | /// Before calling this, modify the `data` property. |
| 45 | pub fn resolve(self: *Self) void { | 55 | pub fn resolve(self: *Self) void { |