| ... | @@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator; | ... | @@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator; |
| 4 | const Compilation = @import("Compilation.zig"); | 4 | const Compilation = @import("Compilation.zig"); |
| 5 | const llvm = @import("llvm_bindings.zig"); | 5 | const llvm = @import("llvm_bindings.zig"); |
| 6 | const link = @import("link.zig"); | 6 | const link = @import("link.zig"); |
| | 7 | const log = std.log.scoped(.codegen); |
| 7 | | 8 | |
| 8 | const Module = @import("Module.zig"); | 9 | const Module = @import("Module.zig"); |
| 9 | const TypedValue = @import("TypedValue.zig"); | 10 | const TypedValue = @import("TypedValue.zig"); |
| ... | @@ -288,8 +289,7 @@ pub const LLVMIRModule = struct { | ... | @@ -288,8 +289,7 @@ pub const LLVMIRModule = struct { |
| 288 | } | 289 | } |
| 289 | | 290 | |
| 290 | pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { | 291 | pub fn updateDecl(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { |
| 291 | const typed_value = decl.typed_value.most_recent.typed_value; | 292 | self.gen(module, decl) catch |err| switch (err) { |
| 292 | self.gen(module, typed_value, decl.src()) catch |err| switch (err) { | | |
| 293 | error.CodegenFail => { | 293 | error.CodegenFail => { |
| 294 | decl.analysis = .codegen_failure; | 294 | decl.analysis = .codegen_failure; |
| 295 | try module.failed_decls.put(module.gpa, decl, self.err_msg.?); | 295 | try module.failed_decls.put(module.gpa, decl, self.err_msg.?); |
| ... | @@ -300,11 +300,16 @@ pub const LLVMIRModule = struct { | ... | @@ -300,11 +300,16 @@ pub const LLVMIRModule = struct { |
| 300 | }; | 300 | }; |
| 301 | } | 301 | } |
| 302 | | 302 | |
| 303 | fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void { | 303 | fn gen(self: *LLVMIRModule, module: *Module, decl: *Module.Decl) !void { |
| 304 | if (typed_value.val.castTag(.function)) |func_inst| { | 304 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 305 | const func = func_inst.data; | 305 | const src = decl.src(); |
| | 306 | |
| | 307 | log.debug("gen: {s} type: {}, value: {}", .{ decl.name, typed_value.ty, typed_value.val }); |
| | 308 | |
| | 309 | if (typed_value.val.castTag(.function)) |func_payload| { |
| | 310 | const func = func_payload.data; |
| 306 | | 311 | |
| 307 | const llvm_func = try self.resolveLLVMFunction(func, src); | 312 | const llvm_func = try self.resolveLLVMFunction(func.owner_decl, src); |
| 308 | | 313 | |
| 309 | // This gets the LLVM values from the function and stores them in `self.args`. | 314 | // This gets the LLVM values from the function and stores them in `self.args`. |
| 310 | const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); | 315 | const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); |
| ... | @@ -355,48 +360,55 @@ pub const LLVMIRModule = struct { | ... | @@ -355,48 +360,55 @@ pub const LLVMIRModule = struct { |
| 355 | }; | 360 | }; |
| 356 | if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val); | 361 | if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val); |
| 357 | } | 362 | } |
| | 363 | } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| { |
| | 364 | _ = try self.resolveLLVMFunction(extern_fn.data, src); |
| 358 | } else { | 365 | } else { |
| 359 | return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{typed_value.ty}); | 366 | _ = try self.resolveGlobalDecl(decl, src); |
| 360 | } | 367 | } |
| 361 | } | 368 | } |
| 362 | | 369 | |
| 363 | fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef { | 370 | fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef { |
| 364 | if (inst.func.value()) |func_value| { | 371 | if (inst.func.value()) |func_value| { |
| 365 | if (func_value.castTag(.function)) |func_payload| { | 372 | const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| |
| 366 | const func = func_payload.data; | 373 | extern_fn.data |
| 367 | const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty; | 374 | else if (func_value.castTag(.function)) |func_payload| |
| 368 | const llvm_fn = try self.resolveLLVMFunction(func, inst.base.src); | 375 | func_payload.data.owner_decl |
| | 376 | else |
| | 377 | unreachable; |
| 369 | | 378 | |
| 370 | const num_args = inst.args.len; | 379 | const zig_fn_type = fn_decl.typed_value.most_recent.typed_value.ty; |
| | 380 | const llvm_fn = try self.resolveLLVMFunction(fn_decl, inst.base.src); |
| 371 | | 381 | |
| 372 | const llvm_param_vals = try self.gpa.alloc(*const llvm.ValueRef, num_args); | 382 | const num_args = inst.args.len; |
| 373 | defer self.gpa.free(llvm_param_vals); | | |
| 374 | | 383 | |
| 375 | for (inst.args) |arg, i| { | 384 | const llvm_param_vals = try self.gpa.alloc(*const llvm.ValueRef, num_args); |
| 376 | llvm_param_vals[i] = try self.resolveInst(arg); | 385 | defer self.gpa.free(llvm_param_vals); |
| 377 | } | | |
| 378 | | 386 | |
| 379 | // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs | 387 | for (inst.args) |arg, i| { |
| 380 | // Do we need that? | 388 | llvm_param_vals[i] = try self.resolveInst(arg); |
| 381 | const call = self.builder.buildCall( | 389 | } |
| 382 | llvm_fn, | | |
| 383 | if (num_args == 0) null else llvm_param_vals.ptr, | | |
| 384 | @intCast(c_uint, num_args), | | |
| 385 | "", | | |
| 386 | ); | | |
| 387 | | | |
| 388 | const return_type = zig_fn_type.fnReturnType(); | | |
| 389 | if (return_type.tag() == .noreturn) { | | |
| 390 | _ = self.builder.buildUnreachable(); | | |
| 391 | } | | |
| 392 | | 390 | |
| 393 | // No need to store the LLVM value if the return type is void or noreturn | 391 | // TODO: LLVMBuildCall2 handles opaque function pointers, according to llvm docs |
| 394 | if (!return_type.hasCodeGenBits()) return null; | 392 | // Do we need that? |
| | 393 | const call = self.builder.buildCall( |
| | 394 | llvm_fn, |
| | 395 | if (num_args == 0) null else llvm_param_vals.ptr, |
| | 396 | @intCast(c_uint, num_args), |
| | 397 | "", |
| | 398 | ); |
| 395 | | 399 | |
| 396 | return call; | 400 | const return_type = zig_fn_type.fnReturnType(); |
| | 401 | if (return_type.tag() == .noreturn) { |
| | 402 | _ = self.builder.buildUnreachable(); |
| 397 | } | 403 | } |
| | 404 | |
| | 405 | // No need to store the LLVM value if the return type is void or noreturn |
| | 406 | if (!return_type.hasCodeGenBits()) return null; |
| | 407 | |
| | 408 | return call; |
| | 409 | } else { |
| | 410 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{}); |
| 398 | } | 411 | } |
| 399 | return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{}); | | |
| 400 | } | 412 | } |
| 401 | | 413 | |
| 402 | fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef { | 414 | fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef { |
| ... | @@ -515,7 +527,7 @@ pub const LLVMIRModule = struct { | ... | @@ -515,7 +527,7 @@ pub const LLVMIRModule = struct { |
| 515 | return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{}); | 527 | return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{}); |
| 516 | } | 528 | } |
| 517 | | 529 | |
| 518 | fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) !*const llvm.ValueRef { | 530 | fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) error{ OutOfMemory, CodegenFail }!*const llvm.ValueRef { |
| 519 | const llvm_type = try self.getLLVMType(tv.ty, src); | 531 | const llvm_type = try self.getLLVMType(tv.ty, src); |
| 520 | | 532 | |
| 521 | if (tv.val.isUndef()) | 533 | if (tv.val.isUndef()) |
| ... | @@ -538,16 +550,89 @@ pub const LLVMIRModule = struct { | ... | @@ -538,16 +550,89 @@ pub const LLVMIRModule = struct { |
| 538 | } | 550 | } |
| 539 | return llvm_int; | 551 | return llvm_int; |
| 540 | }, | 552 | }, |
| | 553 | .Pointer => switch (tv.val.tag()) { |
| | 554 | .decl_ref => { |
| | 555 | const decl = tv.val.castTag(.decl_ref).?.data; |
| | 556 | const val = try self.resolveGlobalDecl(decl, src); |
| | 557 | |
| | 558 | const usize_type = try self.getLLVMType(Type.initTag(.usize), src); |
| | 559 | |
| | 560 | // TODO: second index should be the index into the memory! |
| | 561 | var indices: [2]*const llvm.ValueRef = .{ |
| | 562 | usize_type.constNull(), |
| | 563 | usize_type.constNull(), |
| | 564 | }; |
| | 565 | |
| | 566 | // TODO: consider using buildInBoundsGEP2 for opaque pointers |
| | 567 | return self.builder.buildInBoundsGEP(val, &indices, 2, ""); |
| | 568 | }, |
| | 569 | else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), |
| | 570 | }, |
| | 571 | .Array => { |
| | 572 | if (tv.val.castTag(.bytes)) |payload| { |
| | 573 | const zero_sentinel = if (tv.ty.sentinel()) |sentinel| blk: { |
| | 574 | if (sentinel.tag() == .zero) break :blk true; |
| | 575 | return self.fail(src, "TODO handle other sentinel values", .{}); |
| | 576 | } else false; |
| | 577 | |
| | 578 | return llvm.constString(payload.data.ptr, @intCast(c_uint, payload.data.len), !zero_sentinel); |
| | 579 | } else { |
| | 580 | return self.fail(src, "TODO handle more array values", .{}); |
| | 581 | } |
| | 582 | }, |
| 541 | else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), | 583 | else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), |
| 542 | } | 584 | } |
| 543 | } | 585 | } |
| 544 | | 586 | |
| | 587 | fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef { |
| | 588 | switch (t.zigTypeTag()) { |
| | 589 | .Void => return llvm.voidType(), |
| | 590 | .NoReturn => return llvm.voidType(), |
| | 591 | .Int => { |
| | 592 | const info = t.intInfo(self.module.getTarget()); |
| | 593 | return llvm.intType(info.bits); |
| | 594 | }, |
| | 595 | .Bool => return llvm.intType(1), |
| | 596 | .Pointer => { |
| | 597 | if (t.isSlice()) { |
| | 598 | return self.fail(src, "TODO: LLVM backend: implement slices", .{}); |
| | 599 | } else { |
| | 600 | const elem_type = try self.getLLVMType(t.elemType(), src); |
| | 601 | return elem_type.pointerType(0); |
| | 602 | } |
| | 603 | }, |
| | 604 | .Array => { |
| | 605 | const elem_type = try self.getLLVMType(t.elemType(), src); |
| | 606 | return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); |
| | 607 | }, |
| | 608 | else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), |
| | 609 | } |
| | 610 | } |
| | 611 | |
| | 612 | fn resolveGlobalDecl(self: *LLVMIRModule, decl: *Module.Decl, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.ValueRef { |
| | 613 | // TODO: do we want to store this in our own datastructure? |
| | 614 | if (self.llvm_module.getNamedGlobal(decl.name)) |val| return val; |
| | 615 | |
| | 616 | const typed_value = decl.typed_value.most_recent.typed_value; |
| | 617 | |
| | 618 | // TODO: remove this redundant `getLLVMType`, it is also called in `genTypedValue`. |
| | 619 | const llvm_type = try self.getLLVMType(typed_value.ty, src); |
| | 620 | const val = try self.genTypedValue(src, typed_value); |
| | 621 | const global = self.llvm_module.addGlobal(llvm_type, decl.name); |
| | 622 | llvm.setInitializer(global, val); |
| | 623 | |
| | 624 | // TODO ask the Decl if it is const |
| | 625 | // https://github.com/ziglang/zig/issues/7582 |
| | 626 | |
| | 627 | return global; |
| | 628 | } |
| | 629 | |
| 545 | /// If the llvm function does not exist, create it | 630 | /// If the llvm function does not exist, create it |
| 546 | fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn, src: usize) !*const llvm.ValueRef { | 631 | fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Decl, src: usize) !*const llvm.ValueRef { |
| 547 | // TODO: do we want to store this in our own datastructure? | 632 | // TODO: do we want to store this in our own datastructure? |
| 548 | if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn; | 633 | if (self.llvm_module.getNamedFunction(func.name)) |llvm_fn| return llvm_fn; |
| 549 | | 634 | |
| 550 | const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty; | 635 | const zig_fn_type = func.typed_value.most_recent.typed_value.ty; |
| 551 | const return_type = zig_fn_type.fnReturnType(); | 636 | const return_type = zig_fn_type.fnReturnType(); |
| 552 | | 637 | |
| 553 | const fn_param_len = zig_fn_type.fnParamLen(); | 638 | const fn_param_len = zig_fn_type.fnParamLen(); |
| ... | @@ -569,7 +654,7 @@ pub const LLVMIRModule = struct { | ... | @@ -569,7 +654,7 @@ pub const LLVMIRModule = struct { |
| 569 | @intCast(c_uint, fn_param_len), | 654 | @intCast(c_uint, fn_param_len), |
| 570 | false, | 655 | false, |
| 571 | ); | 656 | ); |
| 572 | const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type); | 657 | const llvm_fn = self.llvm_module.addFunction(func.name, fn_type); |
| 573 | | 658 | |
| 574 | if (return_type.tag() == .noreturn) { | 659 | if (return_type.tag() == .noreturn) { |
| 575 | llvm_fn.addFnAttr("noreturn"); | 660 | llvm_fn.addFnAttr("noreturn"); |
| ... | @@ -578,24 +663,6 @@ pub const LLVMIRModule = struct { | ... | @@ -578,24 +663,6 @@ pub const LLVMIRModule = struct { |
| 578 | return llvm_fn; | 663 | return llvm_fn; |
| 579 | } | 664 | } |
| 580 | | 665 | |
| 581 | fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef { | | |
| 582 | switch (t.zigTypeTag()) { | | |
| 583 | .Void => return llvm.voidType(), | | |
| 584 | .NoReturn => return llvm.voidType(), | | |
| 585 | .Int => { | | |
| 586 | const info = t.intInfo(self.module.getTarget()); | | |
| 587 | return llvm.intType(info.bits); | | |
| 588 | }, | | |
| 589 | .Bool => return llvm.intType(1), | | |
| 590 | .Pointer => { | | |
| 591 | const pointer = t.castPointer().?; | | |
| 592 | const elem_type = try self.getLLVMType(pointer.data, src); | | |
| 593 | return elem_type.pointerType(0); | | |
| 594 | }, | | |
| 595 | else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), | | |
| 596 | } | | |
| 597 | } | | |
| 598 | | | |
| 599 | pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { | 666 | pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { |
| 600 | @setCold(true); | 667 | @setCold(true); |
| 601 | assert(self.err_msg == null); | 668 | assert(self.err_msg == null); |