authorgravatar for timonkruiper@gmail.comTimon Kruiper <timonkruiper@gmail.com> 2021-01-09 16:22:43+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-02 19:02:55-07:00
logd4ec0279d3ec59cf33b1b2fbf74f9ae82af753cc
treec5c261faf4ec7afa5327aeca94ce6289e86de1bf
parent3ad9cb8b473820ec5ea11d85aa72e8ddc83cfa03

stage2: add support for optionals in the LLVM backend

We can now codegen optionals! This includes the following instructions: - is_null - is_null_ptr - is_non_null - is_non_null_ptr - optional_payload - optional_payload_ptr - br_void Also includes a test for optionals.

4 files changed, 176 insertions(+), 15 deletions(-)

src/astgen.zig+22-7
...@@ -453,13 +453,23 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In...@@ -453,13 +453,23 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
453 return rvalue(mod, scope, rl, result);453 return rvalue(mod, scope, rl, result);
454 },454 },
455 .unwrap_optional => {455 .unwrap_optional => {
456 const operand = try expr(mod, scope, rl, node_datas[node].lhs);
457 const op: zir.Inst.Tag = switch (rl) {
458 .ref => .optional_payload_safe_ptr,
459 else => .optional_payload_safe,
460 };
461 const src = token_starts[main_tokens[node]];456 const src = token_starts[main_tokens[node]];
462 return addZIRUnOp(mod, scope, src, op, operand);457 switch (rl) {
458 .ref => return addZIRUnOp(
459 mod,
460 scope,
461 src,
462 .optional_payload_safe_ptr,
463 try expr(mod, scope, .ref, node_datas[node].lhs),
464 ),
465 else => return rvalue(mod, scope, rl, try addZIRUnOp(
466 mod,
467 scope,
468 src,
469 .optional_payload_safe,
470 try expr(mod, scope, .none, node_datas[node].lhs),
471 )),
472 }
463 },473 },
464 .block_two, .block_two_semicolon => {474 .block_two, .block_two_semicolon => {
465 const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };475 const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
...@@ -1701,7 +1711,12 @@ fn orelseCatchExpr(...@@ -1701,7 +1711,12 @@ fn orelseCatchExpr(
17011711
1702 // This could be a pointer or value depending on the `rl` parameter.1712 // This could be a pointer or value depending on the `rl` parameter.
1703 block_scope.break_count += 1;1713 block_scope.break_count += 1;
1704 const operand = try expr(mod, &block_scope.base, block_scope.break_result_loc, lhs);1714 const operand = try expr(
1715 mod,
1716 &block_scope.base,
1717 if (block_scope.break_result_loc == .ref) .ref else .none,
1718 lhs,
1719 );
1705 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);1720 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
17061721
1707 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{1722 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
src/codegen/llvm.zig+105-8
...@@ -397,6 +397,7 @@ pub const LLVMIRModule = struct {...@@ -397,6 +397,7 @@ pub const LLVMIRModule = struct {
397 .block => try self.genBlock(inst.castTag(.block).?),397 .block => try self.genBlock(inst.castTag(.block).?),
398 .br => try self.genBr(inst.castTag(.br).?),398 .br => try self.genBr(inst.castTag(.br).?),
399 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),399 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
400 .br_void => try self.genBrVoid(inst.castTag(.br_void).?),
400 .call => try self.genCall(inst.castTag(.call).?),401 .call => try self.genCall(inst.castTag(.call).?),
401 .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq),402 .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq),
402 .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt),403 .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt),
...@@ -406,6 +407,10 @@ pub const LLVMIRModule = struct {...@@ -406,6 +407,10 @@ pub const LLVMIRModule = struct {
406 .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq),407 .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq),
407 .condbr => try self.genCondBr(inst.castTag(.condbr).?),408 .condbr => try self.genCondBr(inst.castTag(.condbr).?),
408 .intcast => try self.genIntCast(inst.castTag(.intcast).?),409 .intcast => try self.genIntCast(inst.castTag(.intcast).?),
410 .is_non_null => try self.genIsNonNull(inst.castTag(.is_non_null).?, false),
411 .is_non_null_ptr => try self.genIsNonNull(inst.castTag(.is_non_null_ptr).?, true),
412 .is_null => try self.genIsNull(inst.castTag(.is_null).?, false),
413 .is_null_ptr => try self.genIsNull(inst.castTag(.is_null_ptr).?, true),
409 .load => try self.genLoad(inst.castTag(.load).?),414 .load => try self.genLoad(inst.castTag(.load).?),
410 .loop => try self.genLoop(inst.castTag(.loop).?),415 .loop => try self.genLoop(inst.castTag(.loop).?),
411 .not => try self.genNot(inst.castTag(.not).?),416 .not => try self.genNot(inst.castTag(.not).?),
...@@ -414,6 +419,8 @@ pub const LLVMIRModule = struct {...@@ -414,6 +419,8 @@ pub const LLVMIRModule = struct {
414 .store => try self.genStore(inst.castTag(.store).?),419 .store => try self.genStore(inst.castTag(.store).?),
415 .sub => try self.genSub(inst.castTag(.sub).?),420 .sub => try self.genSub(inst.castTag(.sub).?),
416 .unreach => self.genUnreach(inst.castTag(.unreach).?),421 .unreach => self.genUnreach(inst.castTag(.unreach).?),
422 .optional_payload => try self.genOptionalPayload(inst.castTag(.optional_payload).?, false),
423 .optional_payload_ptr => try self.genOptionalPayload(inst.castTag(.optional_payload_ptr).?, true),
417 .dbg_stmt => blk: {424 .dbg_stmt => blk: {
418 // TODO: implement debug info425 // TODO: implement debug info
419 break :blk null;426 break :blk null;
...@@ -534,21 +541,29 @@ pub const LLVMIRModule = struct {...@@ -534,21 +541,29 @@ pub const LLVMIRModule = struct {
534 }541 }
535542
536 fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value {543 fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value {
537 // Get the block that we want to break to.
538 var block = self.blocks.get(inst.block).?;544 var block = self.blocks.get(inst.block).?;
539 _ = self.builder.buildBr(block.parent_bb);
540545
541 // If the break doesn't break a value, then we don't have to add546 // If the break doesn't break a value, then we don't have to add
542 // the values to the lists.547 // the values to the lists.
543 if (!inst.operand.ty.hasCodeGenBits()) return null;548 if (!inst.operand.ty.hasCodeGenBits()) {
549 // TODO: in astgen these instructions should turn into `br_void` instructions.
550 _ = self.builder.buildBr(block.parent_bb);
551 } else {
552 const val = try self.resolveInst(inst.operand);
544553
545 // For the phi node, we need the basic blocks and the values of the554 // For the phi node, we need the basic blocks and the values of the
546 // break instructions.555 // break instructions.
547 try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());556 try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
557 try block.break_vals.append(self.gpa, val);
548558
549 const val = try self.resolveInst(inst.operand);559 _ = self.builder.buildBr(block.parent_bb);
550 try block.break_vals.append(self.gpa, val);560 }
561 return null;
562 }
551563
564 fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value {
565 var block = self.blocks.get(inst.block).?;
566 _ = self.builder.buildBr(block.parent_bb);
552 return null;567 return null;
553 }568 }
554569
...@@ -591,6 +606,44 @@ pub const LLVMIRModule = struct {...@@ -591,6 +606,44 @@ pub const LLVMIRModule = struct {
591 return null;606 return null;
592 }607 }
593608
609 fn genIsNonNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
610 const operand = try self.resolveInst(inst.operand);
611
612 if (operand_is_ptr) {
613 const index_type = self.context.intType(32);
614
615 var indices: [2]*const llvm.Value = .{
616 index_type.constNull(),
617 index_type.constInt(1, false),
618 };
619
620 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");
621 } else {
622 return self.builder.buildExtractValue(operand, 1, "");
623 }
624 }
625
626 fn genIsNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
627 return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, "");
628 }
629
630 fn genOptionalPayload(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
631 const operand = try self.resolveInst(inst.operand);
632
633 if (operand_is_ptr) {
634 const index_type = self.context.intType(32);
635
636 var indices: [2]*const llvm.Value = .{
637 index_type.constNull(),
638 index_type.constNull(),
639 };
640
641 return self.builder.buildInBoundsGEP(operand, &indices, 2, "");
642 } else {
643 return self.builder.buildExtractValue(operand, 0, "");
644 }
645 }
646
594 fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {647 fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
595 const lhs = try self.resolveInst(inst.lhs);648 const lhs = try self.resolveInst(inst.lhs);
596 const rhs = try self.resolveInst(inst.rhs);649 const rhs = try self.resolveInst(inst.rhs);
...@@ -751,6 +804,13 @@ pub const LLVMIRModule = struct {...@@ -751,6 +804,13 @@ pub const LLVMIRModule = struct {
751 // TODO: consider using buildInBoundsGEP2 for opaque pointers804 // TODO: consider using buildInBoundsGEP2 for opaque pointers
752 return self.builder.buildInBoundsGEP(val, &indices, 2, "");805 return self.builder.buildInBoundsGEP(val, &indices, 2, "");
753 },806 },
807 .ref_val => {
808 const elem_value = tv.val.castTag(.ref_val).?.data;
809 const elem_type = tv.ty.castPointer().?.data;
810 const alloca = self.buildAlloca(try self.getLLVMType(elem_type, src));
811 _ = self.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }), alloca);
812 return alloca;
813 },
754 else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}),814 else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}),
755 },815 },
756 .Array => {816 .Array => {
...@@ -765,6 +825,29 @@ pub const LLVMIRModule = struct {...@@ -765,6 +825,29 @@ pub const LLVMIRModule = struct {
765 return self.fail(src, "TODO handle more array values", .{});825 return self.fail(src, "TODO handle more array values", .{});
766 }826 }
767 },827 },
828 .Optional => {
829 if (!tv.ty.isPtrLikeOptional()) {
830 var buf: Type.Payload.ElemType = undefined;
831 const child_type = tv.ty.optionalChild(&buf);
832 const llvm_child_type = try self.getLLVMType(child_type, src);
833
834 if (tv.val.tag() == .null_value) {
835 var optional_values: [2]*const llvm.Value = .{
836 llvm_child_type.constNull(),
837 self.context.intType(1).constNull(),
838 };
839 return self.context.constStruct(&optional_values, 2, false);
840 } else {
841 var optional_values: [2]*const llvm.Value = .{
842 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),
843 self.context.intType(1).constAllOnes(),
844 };
845 return self.context.constStruct(&optional_values, 2, false);
846 }
847 } else {
848 return self.fail(src, "TODO implement const of optional pointer", .{});
849 }
850 },
768 else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),851 else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
769 }852 }
770 }853 }
...@@ -790,6 +873,20 @@ pub const LLVMIRModule = struct {...@@ -790,6 +873,20 @@ pub const LLVMIRModule = struct {
790 const elem_type = try self.getLLVMType(t.elemType(), src);873 const elem_type = try self.getLLVMType(t.elemType(), src);
791 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));874 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
792 },875 },
876 .Optional => {
877 if (!t.isPtrLikeOptional()) {
878 var buf: Type.Payload.ElemType = undefined;
879 const child_type = t.optionalChild(&buf);
880
881 var optional_types: [2]*const llvm.Type = .{
882 try self.getLLVMType(child_type, src),
883 self.context.intType(1),
884 };
885 return self.context.structType(&optional_types, 2, false);
886 } else {
887 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});
888 }
889 },
793 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),890 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
794 }891 }
795 }892 }
src/codegen/llvm/bindings.zig+9
...@@ -21,9 +21,15 @@ pub const Context = opaque {...@@ -21,9 +21,15 @@ pub const Context = opaque {
21 pub const voidType = LLVMVoidTypeInContext;21 pub const voidType = LLVMVoidTypeInContext;
22 extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;22 extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;
2323
24 pub const structType = LLVMStructTypeInContext;
25 extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type;
26
24 pub const constString = LLVMConstStringInContext;27 pub const constString = LLVMConstStringInContext;
25 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value;28 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value;
2629
30 pub const constStruct = LLVMConstStructInContext;
31 extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value;
32
27 pub const createBasicBlock = LLVMCreateBasicBlockInContext;33 pub const createBasicBlock = LLVMCreateBasicBlockInContext;
28 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;34 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;
2935
...@@ -204,6 +210,9 @@ pub const Builder = opaque {...@@ -204,6 +210,9 @@ pub const Builder = opaque {
204210
205 pub const buildPhi = LLVMBuildPhi;211 pub const buildPhi = LLVMBuildPhi;
206 extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value;212 extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value;
213
214 pub const buildExtractValue = LLVMBuildExtractValue;
215 extern fn LLVMBuildExtractValue(*const Builder, AggVal: *const Value, Index: c_uint, Name: [*:0]const u8) *const Value;
207};216};
208217
209pub const IntPredicate = extern enum {218pub const IntPredicate = extern enum {
test/stage2/llvm.zig+40
...@@ -132,4 +132,44 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -132,4 +132,44 @@ pub fn addCases(ctx: *TestContext) !void {
132 \\}132 \\}
133 , "");133 , "");
134 }134 }
135
136 {
137 var case = ctx.exeUsingLlvmBackend("optionals", linux_x64);
138
139 case.addCompareOutput(
140 \\fn assert(ok: bool) void {
141 \\ if (!ok) unreachable;
142 \\}
143 \\
144 \\export fn main() c_int {
145 \\ var opt_val: ?i32 = 10;
146 \\ var null_val: ?i32 = null;
147 \\
148 \\ var val1: i32 = opt_val.?;
149 \\ const val1_1: i32 = opt_val.?;
150 \\ var ptr_val1 = &(opt_val.?);
151 \\ const ptr_val1_1 = &(opt_val.?);
152 \\
153 \\ var val2: i32 = null_val orelse 20;
154 \\ const val2_2: i32 = null_val orelse 20;
155 \\
156 \\ var value: i32 = 20;
157 \\ var ptr_val2 = &(null_val orelse value);
158 \\
159 \\ const val3 = opt_val orelse 30;
160 \\
161 \\ assert(val1 == 10);
162 \\ assert(val1_1 == 10);
163 \\ assert(ptr_val1.* == 10);
164 \\ assert(ptr_val1_1.* == 10);
165 \\
166 \\ assert(val2 == 20);
167 \\ assert(val2_2 == 20);
168 \\ assert(ptr_val2.* == 20);
169 \\
170 \\ assert(val3 == 10);
171 \\ return 0;
172 \\}
173 , "");
174 }
135}175}