authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-09 12:32:10-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-01-09 12:32:10-08:00
log5c49a137d5948310d8abbf9d6f1fb899e7122239
treeb32e6b682bba1e5cd0528abb00fc92fba9ddb4f4
parente4b8148e9c0dc8e1e523965456a59d469e93a26c
parent56c059077cdaf71220cb44f06902051a34ffd31d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7725 from FireFox317/even-more-llvm

stage2: initial implementation of control flow in LLVM backend + TZIR printing

5 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");
5const llvm = @import("llvm/bindings.zig");5const llvm = @import("llvm/bindings.zig");
6const link = @import("../link.zig");6const link = @import("../link.zig");
7const log = std.log.scoped(.codegen);7const log = std.log.scoped(.codegen);
8const math = std.math;
89
9const Module = @import("../Module.zig");10const Module = @import("../Module.zig");
10const TypedValue = @import("../TypedValue.zig");11const TypedValue = @import("../TypedValue.zig");
...@@ -154,6 +155,8 @@ pub const LLVMIRModule = struct {...@@ -154,6 +155,8 @@ pub const LLVMIRModule = struct {
154155
155 /// This stores the LLVM values used in a function, such that they can be156 /// 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) = .{},
158161
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,
167170
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);
254269
270 self.blocks.deinit(self.gpa);
271
255 allocator.destroy(self);272 allocator.destroy(self);
256 }273 }
257274
...@@ -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;
352370
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 }
384378
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 }
438468
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 to632 /// 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);
525650
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;
2626
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;
2932
...@@ -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;
4043
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 {
183192
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
209pub 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};
187221
188pub const BasicBlock = opaque {222pub 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 {
19151915
1916 const InstTable = std.AutoArrayHashMap(*ir.Inst, usize);1916 const InstTable = std.AutoArrayHashMap(*ir.Inst, usize);
19171917
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 },
19832001
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 printing2014 // 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 }
20082025
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 },
21692186
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 printing2232 // 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}