| author | |
| committer | |
| log | 5c49a137d5948310d8abbf9d6f1fb899e7122239 |
| tree | b32e6b682bba1e5cd0528abb00fc92fba9ddb4f4 |
| parent | e4b8148e9c0dc8e1e523965456a59d469e93a26c |
| parent | 56c059077cdaf71220cb44f06902051a34ffd31d |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
stage2: initial implementation of control flow in LLVM backend + TZIR printing5 files changed, 334 insertions(+), 42 deletions(-)
src/codegen/llvm.zig+151-26| ... | @@ -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; |
src/codegen/llvm/bindings.zig+34| ... | @@ -24,6 +24,9 @@ pub const Context = opaque { | ... | @@ -24,6 +24,9 @@ pub const Context = opaque { |
| 24 | pub const constString = LLVMConstStringInContext; | 24 | pub const constString = LLVMConstStringInContext; |
| 25 | extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; | 25 | extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; |
| 26 | 26 | ||
| 27 | pub const createBasicBlock = LLVMCreateBasicBlockInContext; | ||
| 28 | extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; | ||
| 29 | |||
| 27 | pub const appendBasicBlock = LLVMAppendBasicBlockInContext; | 30 | pub const appendBasicBlock = LLVMAppendBasicBlockInContext; |
| 28 | extern fn LLVMAppendBasicBlockInContext(C: *const Context, Fn: *const Value, Name: [*:0]const u8) *const BasicBlock; | 31 | extern fn LLVMAppendBasicBlockInContext(C: *const Context, Fn: *const Value, Name: [*:0]const u8) *const BasicBlock; |
| 29 | 32 | ||
| ... | @@ -38,6 +41,12 @@ pub const Value = opaque { | ... | @@ -38,6 +41,12 @@ pub const Value = opaque { |
| 38 | pub const getFirstBasicBlock = LLVMGetFirstBasicBlock; | 41 | pub const getFirstBasicBlock = LLVMGetFirstBasicBlock; |
| 39 | extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock; | 42 | extern fn LLVMGetFirstBasicBlock(Fn: *const Value) ?*const BasicBlock; |
| 40 | 43 | ||
| 44 | pub const appendExistingBasicBlock = LLVMAppendExistingBasicBlock; | ||
| 45 | extern fn LLVMAppendExistingBasicBlock(Fn: *const Value, BB: *const BasicBlock) void; | ||
| 46 | |||
| 47 | pub const addIncoming = LLVMAddIncoming; | ||
| 48 | extern fn LLVMAddIncoming(PhiNode: *const Value, IncomingValues: [*]*const Value, IncomingBlocks: [*]*const BasicBlock, Count: c_uint) void; | ||
| 49 | |||
| 41 | pub const getNextInstruction = LLVMGetNextInstruction; | 50 | pub const getNextInstruction = LLVMGetNextInstruction; |
| 42 | extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value; | 51 | extern fn LLVMGetNextInstruction(Inst: *const Value) ?*const Value; |
| 43 | }; | 52 | }; |
| ... | @@ -183,6 +192,31 @@ pub const Builder = opaque { | ... | @@ -183,6 +192,31 @@ pub const Builder = opaque { |
| 183 | 192 | ||
| 184 | pub const buildInBoundsGEP = LLVMBuildInBoundsGEP; | 193 | pub const buildInBoundsGEP = LLVMBuildInBoundsGEP; |
| 185 | extern fn LLVMBuildInBoundsGEP(B: *const Builder, Pointer: *const Value, Indices: [*]*const Value, NumIndices: c_uint, Name: [*:0]const u8) *const Value; | 194 | extern fn LLVMBuildInBoundsGEP(B: *const Builder, Pointer: *const Value, Indices: [*]*const Value, NumIndices: c_uint, Name: [*:0]const u8) *const Value; |
| 195 | |||
| 196 | pub const buildICmp = LLVMBuildICmp; | ||
| 197 | extern fn LLVMBuildICmp(*const Builder, Op: IntPredicate, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; | ||
| 198 | |||
| 199 | pub const buildBr = LLVMBuildBr; | ||
| 200 | extern fn LLVMBuildBr(*const Builder, Dest: *const BasicBlock) *const Value; | ||
| 201 | |||
| 202 | pub const buildCondBr = LLVMBuildCondBr; | ||
| 203 | extern fn LLVMBuildCondBr(*const Builder, If: *const Value, Then: *const BasicBlock, Else: *const BasicBlock) *const Value; | ||
| 204 | |||
| 205 | pub const buildPhi = LLVMBuildPhi; | ||
| 206 | extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value; | ||
| 207 | }; | ||
| 208 | |||
| 209 | pub const IntPredicate = extern enum { | ||
| 210 | EQ = 32, | ||
| 211 | NE = 33, | ||
| 212 | UGT = 34, | ||
| 213 | UGE = 35, | ||
| 214 | ULT = 36, | ||
| 215 | ULE = 37, | ||
| 216 | SGT = 38, | ||
| 217 | SGE = 39, | ||
| 218 | SLT = 40, | ||
| 219 | SLE = 41, | ||
| 186 | }; | 220 | }; |
| 187 | 221 | ||
| 188 | pub const BasicBlock = opaque { | 222 | pub const BasicBlock = opaque { |
src/test.zig+2| ... | @@ -657,8 +657,10 @@ pub const TestContext = struct { | ... | @@ -657,8 +657,10 @@ pub const TestContext = struct { |
| 657 | .object_format = case.object_format, | 657 | .object_format = case.object_format, |
| 658 | .is_native_os = case.target.isNativeOs(), | 658 | .is_native_os = case.target.isNativeOs(), |
| 659 | .is_native_abi = case.target.isNativeAbi(), | 659 | .is_native_abi = case.target.isNativeAbi(), |
| 660 | .dynamic_linker = target_info.dynamic_linker.get(), | ||
| 660 | .link_libc = case.llvm_backend, | 661 | .link_libc = case.llvm_backend, |
| 661 | .use_llvm = case.llvm_backend, | 662 | .use_llvm = case.llvm_backend, |
| 663 | .use_lld = case.llvm_backend, | ||
| 662 | .self_exe_path = std.testing.zig_exe_path, | 664 | .self_exe_path = std.testing.zig_exe_path, |
| 663 | }); | 665 | }); |
| 664 | defer comp.destroy(); | 666 | defer comp.destroy(); |
src/zir.zig+76-16| ... | @@ -1915,10 +1915,28 @@ const DumpTzir = struct { | ... | @@ -1915,10 +1915,28 @@ const DumpTzir = struct { |
| 1915 | 1915 | ||
| 1916 | const InstTable = std.AutoArrayHashMap(*ir.Inst, usize); | 1916 | const InstTable = std.AutoArrayHashMap(*ir.Inst, usize); |
| 1917 | 1917 | ||
| 1918 | /// TODO: Improve this code to include a stack of ir.Body and store the instructions | ||
| 1919 | /// in there. Now we are putting all the instructions in a function local table, | ||
| 1920 | /// however instructions that are in a Body can be thown away when the Body ends. | ||
| 1918 | fn dump(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void { | 1921 | fn dump(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void { |
| 1919 | // First pass to pre-populate the table so that we can show even invalid references. | 1922 | // First pass to pre-populate the table so that we can show even invalid references. |
| 1920 | // Must iterate the same order we iterate the second time. | 1923 | // Must iterate the same order we iterate the second time. |
| 1921 | // We also look for constants and put them in the const_table. | 1924 | // We also look for constants and put them in the const_table. |
| 1925 | try dtz.fetchInstsAndResolveConsts(body); | ||
| 1926 | |||
| 1927 | std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name}); | ||
| 1928 | |||
| 1929 | for (dtz.const_table.items()) |entry| { | ||
| 1930 | const constant = entry.key.castTag(.constant).?; | ||
| 1931 | try writer.print(" @{d}: {} = {};\n", .{ | ||
| 1932 | entry.value, constant.base.ty, constant.val, | ||
| 1933 | }); | ||
| 1934 | } | ||
| 1935 | |||
| 1936 | return dtz.dumpBody(body, writer); | ||
| 1937 | } | ||
| 1938 | |||
| 1939 | fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: ir.Body) error{OutOfMemory}!void { | ||
| 1922 | for (body.instructions) |inst| { | 1940 | for (body.instructions) |inst| { |
| 1923 | try dtz.inst_table.put(inst, dtz.next_index); | 1941 | try dtz.inst_table.put(inst, dtz.next_index); |
| 1924 | dtz.next_index += 1; | 1942 | dtz.next_index += 1; |
| ... | @@ -1981,11 +1999,21 @@ const DumpTzir = struct { | ... | @@ -1981,11 +1999,21 @@ const DumpTzir = struct { |
| 1981 | try dtz.findConst(&brvoid.block.base); | 1999 | try dtz.findConst(&brvoid.block.base); |
| 1982 | }, | 2000 | }, |
| 1983 | 2001 | ||
| 2002 | .block => { | ||
| 2003 | const block = inst.castTag(.block).?; | ||
| 2004 | try dtz.fetchInstsAndResolveConsts(block.body); | ||
| 2005 | }, | ||
| 2006 | |||
| 2007 | .condbr => { | ||
| 2008 | const condbr = inst.castTag(.condbr).?; | ||
| 2009 | try dtz.findConst(condbr.condition); | ||
| 2010 | try dtz.fetchInstsAndResolveConsts(condbr.then_body); | ||
| 2011 | try dtz.fetchInstsAndResolveConsts(condbr.else_body); | ||
| 2012 | }, | ||
| 2013 | |||
| 1984 | // TODO fill out this debug printing | 2014 | // TODO fill out this debug printing |
| 1985 | .assembly, | 2015 | .assembly, |
| 1986 | .block, | ||
| 1987 | .call, | 2016 | .call, |
| 1988 | .condbr, | ||
| 1989 | .constant, | 2017 | .constant, |
| 1990 | .loop, | 2018 | .loop, |
| 1991 | .varptr, | 2019 | .varptr, |
| ... | @@ -1993,20 +2021,9 @@ const DumpTzir = struct { | ... | @@ -1993,20 +2021,9 @@ const DumpTzir = struct { |
| 1993 | => {}, | 2021 | => {}, |
| 1994 | } | 2022 | } |
| 1995 | } | 2023 | } |
| 1996 | |||
| 1997 | std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name}); | ||
| 1998 | |||
| 1999 | for (dtz.const_table.items()) |entry| { | ||
| 2000 | const constant = entry.key.castTag(.constant).?; | ||
| 2001 | try writer.print(" @{d}: {} = {};\n", .{ | ||
| 2002 | entry.value, constant.base.ty, constant.val, | ||
| 2003 | }); | ||
| 2004 | } | ||
| 2005 | |||
| 2006 | return dtz.dumpBody(body, writer); | ||
| 2007 | } | 2024 | } |
| 2008 | 2025 | ||
| 2009 | fn dumpBody(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void { | 2026 | fn dumpBody(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void { |
| 2010 | for (body.instructions) |inst| { | 2027 | for (body.instructions) |inst| { |
| 2011 | const my_index = dtz.next_partial_index; | 2028 | const my_index = dtz.next_partial_index; |
| 2012 | try dtz.partial_inst_table.put(inst, my_index); | 2029 | try dtz.partial_inst_table.put(inst, my_index); |
| ... | @@ -2167,11 +2184,54 @@ const DumpTzir = struct { | ... | @@ -2167,11 +2184,54 @@ const DumpTzir = struct { |
| 2167 | } | 2184 | } |
| 2168 | }, | 2185 | }, |
| 2169 | 2186 | ||
| 2187 | .block => { | ||
| 2188 | const block = inst.castTag(.block).?; | ||
| 2189 | |||
| 2190 | try writer.writeAll("\n"); | ||
| 2191 | |||
| 2192 | const old_indent = dtz.indent; | ||
| 2193 | dtz.indent += 2; | ||
| 2194 | try dtz.dumpBody(block.body, writer); | ||
| 2195 | dtz.indent = old_indent; | ||
| 2196 | |||
| 2197 | try writer.writeByteNTimes(' ', dtz.indent); | ||
| 2198 | try writer.writeAll(")\n"); | ||
| 2199 | }, | ||
| 2200 | |||
| 2201 | .condbr => { | ||
| 2202 | const condbr = inst.castTag(.condbr).?; | ||
| 2203 | |||
| 2204 | if (dtz.partial_inst_table.get(condbr.condition)) |operand_index| { | ||
| 2205 | try writer.print("%{d},", .{operand_index}); | ||
| 2206 | } else if (dtz.const_table.get(condbr.condition)) |operand_index| { | ||
| 2207 | try writer.print("@{d},", .{operand_index}); | ||
| 2208 | } else if (dtz.inst_table.get(condbr.condition)) |operand_index| { | ||
| 2209 | try writer.print("%{d}, // Instruction does not dominate all uses!", .{operand_index}); | ||
| 2210 | } else { | ||
| 2211 | try writer.writeAll("!BADREF!,"); | ||
| 2212 | } | ||
| 2213 | try writer.writeAll("\n"); | ||
| 2214 | |||
| 2215 | try writer.writeByteNTimes(' ', dtz.indent); | ||
| 2216 | try writer.writeAll("then:\n"); | ||
| 2217 | |||
| 2218 | const old_indent = dtz.indent; | ||
| 2219 | dtz.indent += 2; | ||
| 2220 | try dtz.dumpBody(condbr.then_body, writer); | ||
| 2221 | |||
| 2222 | try writer.writeByteNTimes(' ', old_indent); | ||
| 2223 | try writer.writeAll("else:\n"); | ||
| 2224 | |||
| 2225 | try dtz.dumpBody(condbr.else_body, writer); | ||
| 2226 | dtz.indent = old_indent; | ||
| 2227 | |||
| 2228 | try writer.writeByteNTimes(' ', old_indent); | ||
| 2229 | try writer.writeAll(")\n"); | ||
| 2230 | }, | ||
| 2231 | |||
| 2170 | // TODO fill out this debug printing | 2232 | // TODO fill out this debug printing |
| 2171 | .assembly, | 2233 | .assembly, |
| 2172 | .block, | ||
| 2173 | .call, | 2234 | .call, |
| 2174 | .condbr, | ||
| 2175 | .constant, | 2235 | .constant, |
| 2176 | .loop, | 2236 | .loop, |
| 2177 | .varptr, | 2237 | .varptr, |
test/stage2/llvm.zig+71| ... | @@ -40,4 +40,75 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -40,4 +40,75 @@ pub fn addCases(ctx: *TestContext) !void { |
| 40 | \\} | 40 | \\} |
| 41 | , "hello world!" ++ std.cstr.line_sep); | 41 | , "hello world!" ++ std.cstr.line_sep); |
| 42 | } | 42 | } |
| 43 | |||
| 44 | { | ||
| 45 | var case = ctx.exeUsingLlvmBackend("simple if statement", linux_x64); | ||
| 46 | |||
| 47 | case.addCompareOutput( | ||
| 48 | \\fn add(a: i32, b: i32) i32 { | ||
| 49 | \\ return a + b; | ||
| 50 | \\} | ||
| 51 | \\ | ||
| 52 | \\fn assert(ok: bool) void { | ||
| 53 | \\ if (!ok) unreachable; | ||
| 54 | \\} | ||
| 55 | \\ | ||
| 56 | \\export fn main() c_int { | ||
| 57 | \\ assert(add(1,2) == 3); | ||
| 58 | \\ return 0; | ||
| 59 | \\} | ||
| 60 | , ""); | ||
| 61 | } | ||
| 62 | |||
| 63 | { | ||
| 64 | var case = ctx.exeUsingLlvmBackend("blocks", linux_x64); | ||
| 65 | |||
| 66 | case.addCompareOutput( | ||
| 67 | \\fn assert(ok: bool) void { | ||
| 68 | \\ if (!ok) unreachable; | ||
| 69 | \\} | ||
| 70 | \\ | ||
| 71 | \\fn foo(ok: bool) i32 { | ||
| 72 | \\ const val: i32 = blk: { | ||
| 73 | \\ var x: i32 = 1; | ||
| 74 | \\ if (!ok) break :blk x + 9; | ||
| 75 | \\ break :blk x + 19; | ||
| 76 | \\ }; | ||
| 77 | \\ return val + 10; | ||
| 78 | \\} | ||
| 79 | \\ | ||
| 80 | \\export fn main() c_int { | ||
| 81 | \\ assert(foo(false) == 20); | ||
| 82 | \\ assert(foo(true) == 30); | ||
| 83 | \\ return 0; | ||
| 84 | \\} | ||
| 85 | , ""); | ||
| 86 | } | ||
| 87 | |||
| 88 | { | ||
| 89 | var case = ctx.exeUsingLlvmBackend("nested blocks", linux_x64); | ||
| 90 | |||
| 91 | case.addCompareOutput( | ||
| 92 | \\fn assert(ok: bool) void { | ||
| 93 | \\ if (!ok) unreachable; | ||
| 94 | \\} | ||
| 95 | \\ | ||
| 96 | \\fn foo(ok: bool) i32 { | ||
| 97 | \\ var val: i32 = blk: { | ||
| 98 | \\ const val2: i32 = another: { | ||
| 99 | \\ if (!ok) break :blk 10; | ||
| 100 | \\ break :another 10; | ||
| 101 | \\ }; | ||
| 102 | \\ break :blk val2 + 10; | ||
| 103 | \\ }; | ||
| 104 | \\ return val; | ||
| 105 | \\} | ||
| 106 | \\ | ||
| 107 | \\export fn main() c_int { | ||
| 108 | \\ assert(foo(false) == 10); | ||
| 109 | \\ assert(foo(true) == 20); | ||
| 110 | \\ return 0; | ||
| 111 | \\} | ||
| 112 | , ""); | ||
| 113 | } | ||
| 43 | } | 114 | } |