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
453453 return rvalue(mod, scope, rl, result);
454454 },
455455 .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 };
461456 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 }
463473 },
464474 .block_two, .block_two_semicolon => {
465475 const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
......@@ -1701,7 +1711,12 @@ fn orelseCatchExpr(
17011711
17021712 // This could be a pointer or value depending on the `rl` parameter.
17031713 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 );
17051720 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
17061721
17071722 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 {
397397 .block => try self.genBlock(inst.castTag(.block).?),
398398 .br => try self.genBr(inst.castTag(.br).?),
399399 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
400 .br_void => try self.genBrVoid(inst.castTag(.br_void).?),
400401 .call => try self.genCall(inst.castTag(.call).?),
401402 .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq),
402403 .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt),
......@@ -406,6 +407,10 @@ pub const LLVMIRModule = struct {
406407 .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq),
407408 .condbr => try self.genCondBr(inst.castTag(.condbr).?),
408409 .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),
409414 .load => try self.genLoad(inst.castTag(.load).?),
410415 .loop => try self.genLoop(inst.castTag(.loop).?),
411416 .not => try self.genNot(inst.castTag(.not).?),
......@@ -414,6 +419,8 @@ pub const LLVMIRModule = struct {
414419 .store => try self.genStore(inst.castTag(.store).?),
415420 .sub => try self.genSub(inst.castTag(.sub).?),
416421 .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),
417424 .dbg_stmt => blk: {
418425 // TODO: implement debug info
419426 break :blk null;
......@@ -534,21 +541,29 @@ pub const LLVMIRModule = struct {
534541 }
535542
536543 fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value {
537 // Get the block that we want to break to.
538544 var block = self.blocks.get(inst.block).?;
539 _ = self.builder.buildBr(block.parent_bb);
540545
541546 // If the break doesn't break a value, then we don't have to add
542547 // 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 the
546 // break instructions.
547 try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
554 // For the phi node, we need the basic blocks and the values of the
555 // break instructions.
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);
550 try block.break_vals.append(self.gpa, val);
559 _ = self.builder.buildBr(block.parent_bb);
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);
552567 return null;
553568 }
554569
......@@ -591,6 +606,44 @@ pub const LLVMIRModule = struct {
591606 return null;
592607 }
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
594647 fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
595648 const lhs = try self.resolveInst(inst.lhs);
596649 const rhs = try self.resolveInst(inst.rhs);
......@@ -751,6 +804,13 @@ pub const LLVMIRModule = struct {
751804 // TODO: consider using buildInBoundsGEP2 for opaque pointers
752805 return self.builder.buildInBoundsGEP(val, &indices, 2, "");
753806 },
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 },
754814 else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}),
755815 },
756816 .Array => {
......@@ -765,6 +825,29 @@ pub const LLVMIRModule = struct {
765825 return self.fail(src, "TODO handle more array values", .{});
766826 }
767827 },
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 },
768851 else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
769852 }
770853 }
......@@ -790,6 +873,20 @@ pub const LLVMIRModule = struct {
790873 const elem_type = try self.getLLVMType(t.elemType(), src);
791874 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
792875 },
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 },
793890 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
794891 }
795892 }
src/codegen/llvm/bindings.zig+9
......@@ -21,9 +21,15 @@ pub const Context = opaque {
2121 pub const voidType = LLVMVoidTypeInContext;
2222 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
2427 pub const constString = LLVMConstStringInContext;
2528 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
2733 pub const createBasicBlock = LLVMCreateBasicBlockInContext;
2834 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;
2935
......@@ -204,6 +210,9 @@ pub const Builder = opaque {
204210
205211 pub const buildPhi = LLVMBuildPhi;
206212 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;
207216};
208217
209218pub const IntPredicate = extern enum {
test/stage2/llvm.zig+40
......@@ -132,4 +132,44 @@ pub fn addCases(ctx: *TestContext) !void {
132132 \\}
133133 , "");
134134 }
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 }
135175}