| ... | @@ -5,6 +5,7 @@ const Compilation = @import("../Compilation.zig"); | ... | @@ -5,6 +5,7 @@ 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 | const log = std.log.scoped(.codegen); |
| | 8 | const math = std.math; |
| 8 | | 9 | |
| 9 | const Module = @import("../Module.zig"); | 10 | const Module = @import("../Module.zig"); |
| 10 | const TypedValue = @import("../TypedValue.zig"); | 11 | const TypedValue = @import("../TypedValue.zig"); |
| ... | @@ -154,6 +155,8 @@ pub const LLVMIRModule = struct { | ... | @@ -154,6 +155,8 @@ pub const LLVMIRModule = struct { |
| 154 | | 155 | |
| 155 | /// This stores the LLVM values used in a function, such that they can be | 156 | /// This stores the LLVM values used in a function, such that they can be |
| 156 | /// referred to in other instructions. This table is cleared before every function is generated. | 157 | /// referred to in other instructions. This table is cleared before every function is generated. |
| | 158 | /// TODO: Change this to a stack of Branch. Currently we store all the values from all the blocks |
| | 159 | /// in here, however if a block ends, the instructions can be thrown away. |
| 157 | func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{}, | 160 | func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.Value) = .{}, |
| 158 | | 161 | |
| 159 | /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. | 162 | /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. |
| ... | @@ -165,6 +168,18 @@ pub const LLVMIRModule = struct { | ... | @@ -165,6 +168,18 @@ pub const LLVMIRModule = struct { |
| 165 | /// to the top of the function. | 168 | /// to the top of the function. |
| 166 | latest_alloca_inst: ?*const llvm.Value = null, | 169 | latest_alloca_inst: ?*const llvm.Value = null, |
| 167 | | 170 | |
| | 171 | llvm_func: *const llvm.Value = undefined, |
| | 172 | |
| | 173 | /// This data structure is used to implement breaking to blocks. |
| | 174 | blocks: std.AutoHashMapUnmanaged(*Inst.Block, struct { |
| | 175 | parent_bb: *const llvm.BasicBlock, |
| | 176 | break_bbs: *BreakBasicBlocks, |
| | 177 | break_vals: *BreakValues, |
| | 178 | }) = .{}, |
| | 179 | |
| | 180 | const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock); |
| | 181 | const BreakValues = std.ArrayListUnmanaged(*const llvm.Value); |
| | 182 | |
| 168 | pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule { | 183 | pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule { |
| 169 | const self = try allocator.create(LLVMIRModule); | 184 | const self = try allocator.create(LLVMIRModule); |
| 170 | errdefer allocator.destroy(self); | 185 | errdefer allocator.destroy(self); |
| ... | @@ -252,6 +267,8 @@ pub const LLVMIRModule = struct { | ... | @@ -252,6 +267,8 @@ pub const LLVMIRModule = struct { |
| 252 | self.func_inst_table.deinit(self.gpa); | 267 | self.func_inst_table.deinit(self.gpa); |
| 253 | self.gpa.free(self.object_path); | 268 | self.gpa.free(self.object_path); |
| 254 | | 269 | |
| | 270 | self.blocks.deinit(self.gpa); |
| | 271 | |
| 255 | allocator.destroy(self); | 272 | allocator.destroy(self); |
| 256 | } | 273 | } |
| 257 | | 274 | |
| ... | @@ -349,32 +366,9 @@ pub const LLVMIRModule = struct { | ... | @@ -349,32 +366,9 @@ pub const LLVMIRModule = struct { |
| 349 | self.entry_block = self.context.appendBasicBlock(llvm_func, "Entry"); | 366 | self.entry_block = self.context.appendBasicBlock(llvm_func, "Entry"); |
| 350 | self.builder.positionBuilderAtEnd(self.entry_block); | 367 | self.builder.positionBuilderAtEnd(self.entry_block); |
| 351 | self.latest_alloca_inst = null; | 368 | self.latest_alloca_inst = null; |
| | 369 | self.llvm_func = llvm_func; |
| 352 | | 370 | |
| 353 | const instructions = func.body.instructions; | 371 | try self.genBody(func.body); |
| 354 | for (instructions) |inst| { | | |
| 355 | const opt_llvm_val: ?*const llvm.Value = switch (inst.tag) { | | |
| 356 | .add => try self.genAdd(inst.castTag(.add).?), | | |
| 357 | .alloc => try self.genAlloc(inst.castTag(.alloc).?), | | |
| 358 | .arg => try self.genArg(inst.castTag(.arg).?), | | |
| 359 | .bitcast => try self.genBitCast(inst.castTag(.bitcast).?), | | |
| 360 | .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), | | |
| 361 | .call => try self.genCall(inst.castTag(.call).?), | | |
| 362 | .intcast => try self.genIntCast(inst.castTag(.intcast).?), | | |
| 363 | .load => try self.genLoad(inst.castTag(.load).?), | | |
| 364 | .not => try self.genNot(inst.castTag(.not).?), | | |
| 365 | .ret => try self.genRet(inst.castTag(.ret).?), | | |
| 366 | .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), | | |
| 367 | .store => try self.genStore(inst.castTag(.store).?), | | |
| 368 | .sub => try self.genSub(inst.castTag(.sub).?), | | |
| 369 | .unreach => self.genUnreach(inst.castTag(.unreach).?), | | |
| 370 | .dbg_stmt => blk: { | | |
| 371 | // TODO: implement debug info | | |
| 372 | break :blk null; | | |
| 373 | }, | | |
| 374 | else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), | | |
| 375 | }; | | |
| 376 | if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val); | | |
| 377 | } | | |
| 378 | } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| { | 372 | } else if (typed_value.val.castTag(.extern_fn)) |extern_fn| { |
| 379 | _ = try self.resolveLLVMFunction(extern_fn.data, src); | 373 | _ = try self.resolveLLVMFunction(extern_fn.data, src); |
| 380 | } else { | 374 | } else { |
| ... | @@ -382,6 +376,42 @@ pub const LLVMIRModule = struct { | ... | @@ -382,6 +376,42 @@ pub const LLVMIRModule = struct { |
| 382 | } | 376 | } |
| 383 | } | 377 | } |
| 384 | | 378 | |
| | 379 | fn genBody(self: *LLVMIRModule, body: ir.Body) error{ OutOfMemory, CodegenFail }!void { |
| | 380 | for (body.instructions) |inst| { |
| | 381 | const opt_value = switch (inst.tag) { |
| | 382 | .add => try self.genAdd(inst.castTag(.add).?), |
| | 383 | .alloc => try self.genAlloc(inst.castTag(.alloc).?), |
| | 384 | .arg => try self.genArg(inst.castTag(.arg).?), |
| | 385 | .bitcast => try self.genBitCast(inst.castTag(.bitcast).?), |
| | 386 | .block => try self.genBlock(inst.castTag(.block).?), |
| | 387 | .br => try self.genBr(inst.castTag(.br).?), |
| | 388 | .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), |
| | 389 | .call => try self.genCall(inst.castTag(.call).?), |
| | 390 | .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq), |
| | 391 | .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt), |
| | 392 | .cmp_gte => try self.genCmp(inst.castTag(.cmp_gte).?, .gte), |
| | 393 | .cmp_lt => try self.genCmp(inst.castTag(.cmp_lt).?, .lt), |
| | 394 | .cmp_lte => try self.genCmp(inst.castTag(.cmp_lte).?, .lte), |
| | 395 | .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq), |
| | 396 | .condbr => try self.genCondBr(inst.castTag(.condbr).?), |
| | 397 | .intcast => try self.genIntCast(inst.castTag(.intcast).?), |
| | 398 | .load => try self.genLoad(inst.castTag(.load).?), |
| | 399 | .not => try self.genNot(inst.castTag(.not).?), |
| | 400 | .ret => try self.genRet(inst.castTag(.ret).?), |
| | 401 | .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), |
| | 402 | .store => try self.genStore(inst.castTag(.store).?), |
| | 403 | .sub => try self.genSub(inst.castTag(.sub).?), |
| | 404 | .unreach => self.genUnreach(inst.castTag(.unreach).?), |
| | 405 | .dbg_stmt => blk: { |
| | 406 | // TODO: implement debug info |
| | 407 | break :blk null; |
| | 408 | }, |
| | 409 | else => |tag| return self.fail(inst.src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), |
| | 410 | }; |
| | 411 | if (opt_value) |val| try self.func_inst_table.putNoClobber(self.gpa, inst, val); |
| | 412 | } |
| | 413 | } |
| | 414 | |
| 385 | fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value { | 415 | fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.Value { |
| 386 | if (inst.func.value()) |func_value| { | 416 | if (inst.func.value()) |func_value| { |
| 387 | const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| | 417 | const fn_decl = if (func_value.castTag(.extern_fn)) |extern_fn| |
| ... | @@ -436,6 +466,99 @@ pub const LLVMIRModule = struct { | ... | @@ -436,6 +466,99 @@ pub const LLVMIRModule = struct { |
| 436 | return null; | 466 | return null; |
| 437 | } | 467 | } |
| 438 | | 468 | |
| | 469 | fn genCmp(self: *LLVMIRModule, inst: *Inst.BinOp, op: math.CompareOperator) !?*const llvm.Value { |
| | 470 | const lhs = try self.resolveInst(inst.lhs); |
| | 471 | const rhs = try self.resolveInst(inst.rhs); |
| | 472 | |
| | 473 | if (!inst.base.ty.isInt()) |
| | 474 | if (inst.base.ty.tag() != .bool) |
| | 475 | return self.fail(inst.base.src, "TODO implement 'genCmp' for type {}", .{inst.base.ty}); |
| | 476 | |
| | 477 | const is_signed = inst.base.ty.isSignedInt(); |
| | 478 | const operation = switch (op) { |
| | 479 | .eq => .EQ, |
| | 480 | .neq => .NE, |
| | 481 | .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT), |
| | 482 | .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE), |
| | 483 | .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT), |
| | 484 | .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE), |
| | 485 | }; |
| | 486 | |
| | 487 | return self.builder.buildICmp(operation, lhs, rhs, ""); |
| | 488 | } |
| | 489 | |
| | 490 | fn genBlock(self: *LLVMIRModule, inst: *Inst.Block) !?*const llvm.Value { |
| | 491 | const parent_bb = self.context.createBasicBlock("Block"); |
| | 492 | |
| | 493 | // 5 breaks to a block seems like a reasonable default. |
| | 494 | var break_bbs = try BreakBasicBlocks.initCapacity(self.gpa, 5); |
| | 495 | var break_vals = try BreakValues.initCapacity(self.gpa, 5); |
| | 496 | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| | 497 | .parent_bb = parent_bb, |
| | 498 | .break_bbs = &break_bbs, |
| | 499 | .break_vals = &break_vals, |
| | 500 | }); |
| | 501 | defer { |
| | 502 | self.blocks.removeAssertDiscard(inst); |
| | 503 | break_bbs.deinit(self.gpa); |
| | 504 | break_vals.deinit(self.gpa); |
| | 505 | } |
| | 506 | |
| | 507 | try self.genBody(inst.body); |
| | 508 | |
| | 509 | self.llvm_func.appendExistingBasicBlock(parent_bb); |
| | 510 | self.builder.positionBuilderAtEnd(parent_bb); |
| | 511 | |
| | 512 | // If the block does not return a value, we dont have to create a phi node. |
| | 513 | if (!inst.base.ty.hasCodeGenBits()) return null; |
| | 514 | |
| | 515 | const phi_node = self.builder.buildPhi(try self.getLLVMType(inst.base.ty, inst.base.src), ""); |
| | 516 | phi_node.addIncoming( |
| | 517 | break_vals.items.ptr, |
| | 518 | break_bbs.items.ptr, |
| | 519 | @intCast(c_uint, break_vals.items.len), |
| | 520 | ); |
| | 521 | return phi_node; |
| | 522 | } |
| | 523 | |
| | 524 | fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { |
| | 525 | // Get the block that we want to break to. |
| | 526 | var block = self.blocks.get(inst.block).?; |
| | 527 | _ = self.builder.buildBr(block.parent_bb); |
| | 528 | |
| | 529 | // If the break doesn't break a value, then we don't have to add |
| | 530 | // the values to the lists. |
| | 531 | if (!inst.operand.ty.hasCodeGenBits()) return null; |
| | 532 | |
| | 533 | // For the phi node, we need the basic blocks and the values of the |
| | 534 | // break instructions. |
| | 535 | try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); |
| | 536 | |
| | 537 | const val = try self.resolveInst(inst.operand); |
| | 538 | try block.break_vals.append(self.gpa, val); |
| | 539 | |
| | 540 | return null; |
| | 541 | } |
| | 542 | |
| | 543 | fn genCondBr(self: *LLVMIRModule, inst: *Inst.CondBr) !?*const llvm.Value { |
| | 544 | const condition_value = try self.resolveInst(inst.condition); |
| | 545 | |
| | 546 | const then_block = self.context.appendBasicBlock(self.llvm_func, "Then"); |
| | 547 | const else_block = self.context.appendBasicBlock(self.llvm_func, "Else"); |
| | 548 | { |
| | 549 | const prev_block = self.builder.getInsertBlock(); |
| | 550 | defer self.builder.positionBuilderAtEnd(prev_block); |
| | 551 | |
| | 552 | self.builder.positionBuilderAtEnd(then_block); |
| | 553 | try self.genBody(inst.then_body); |
| | 554 | |
| | 555 | self.builder.positionBuilderAtEnd(else_block); |
| | 556 | try self.genBody(inst.else_body); |
| | 557 | } |
| | 558 | _ = self.builder.buildCondBr(condition_value, then_block, else_block); |
| | 559 | return null; |
| | 560 | } |
| | 561 | |
| 439 | fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { | 562 | fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.Value { |
| 440 | return self.builder.buildNot(try self.resolveInst(inst.operand), ""); | 563 | return self.builder.buildNot(try self.resolveInst(inst.operand), ""); |
| 441 | } | 564 | } |
| ... | @@ -509,6 +632,9 @@ pub const LLVMIRModule = struct { | ... | @@ -509,6 +632,9 @@ pub const LLVMIRModule = struct { |
| 509 | /// Use this instead of builder.buildAlloca, because this function makes sure to | 632 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 510 | /// put the alloca instruction at the top of the function! | 633 | /// put the alloca instruction at the top of the function! |
| 511 | fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value { | 634 | fn buildAlloca(self: *LLVMIRModule, t: *const llvm.Type) *const llvm.Value { |
| | 635 | const prev_block = self.builder.getInsertBlock(); |
| | 636 | defer self.builder.positionBuilderAtEnd(prev_block); |
| | 637 | |
| 512 | if (self.latest_alloca_inst) |latest_alloc| { | 638 | if (self.latest_alloca_inst) |latest_alloc| { |
| 513 | // builder.positionBuilder adds it before the instruction, | 639 | // builder.positionBuilder adds it before the instruction, |
| 514 | // but we want to put it after the last alloca instruction. | 640 | // but we want to put it after the last alloca instruction. |
| ... | @@ -521,7 +647,6 @@ pub const LLVMIRModule = struct { | ... | @@ -521,7 +647,6 @@ pub const LLVMIRModule = struct { |
| 521 | self.builder.positionBuilder(self.entry_block, first_inst); | 647 | self.builder.positionBuilder(self.entry_block, first_inst); |
| 522 | } | 648 | } |
| 523 | } | 649 | } |
| 524 | defer self.builder.positionBuilderAtEnd(self.entry_block); | | |
| 525 | | 650 | |
| 526 | const val = self.builder.buildAlloca(t, ""); | 651 | const val = self.builder.buildAlloca(t, ""); |
| 527 | self.latest_alloca_inst = val; | 652 | self.latest_alloca_inst = val; |