authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-02 21:03:50-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-03-02 21:03:50-08:00
log4b57fb5f23aa6513ceaebe396d1faf215cf0a475
treea6060a67d81b1f875e0cfb947fadec9325b37a89
parent3ad9cb8b473820ec5ea11d85aa72e8ddc83cfa03
parent713f1138222dc40355c34c70d83b0a0805bd46c6
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7741 from FireFox317/optionals-llvm

stage2: add support for optionals in the LLVM backend

11 files changed, 258 insertions(+), 31 deletions(-)

src/astgen.zig+46-9
...@@ -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 };
...@@ -1699,9 +1709,13 @@ fn orelseCatchExpr(...@@ -1699,9 +1709,13 @@ fn orelseCatchExpr(
1699 setBlockResultLoc(&block_scope, rl);1709 setBlockResultLoc(&block_scope, rl);
1700 defer block_scope.instructions.deinit(mod.gpa);1710 defer block_scope.instructions.deinit(mod.gpa);
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 `operand_rl` parameter.
1713 // We cannot use `block_scope.break_result_loc` because that has the bare
1714 // type, whereas this expression has the optional type. Later we make
1715 // up for this fact by calling rvalue on the else branch.
1703 block_scope.break_count += 1;1716 block_scope.break_count += 1;
1704 const operand = try expr(mod, &block_scope.base, block_scope.break_result_loc, lhs);1717 const operand_rl = try makeOptionalTypeResultLoc(mod, &block_scope.base, src, block_scope.break_result_loc);
1718 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
1705 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);1719 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
17061720
1707 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{1721 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
...@@ -1753,6 +1767,10 @@ fn orelseCatchExpr(...@@ -1753,6 +1767,10 @@ fn orelseCatchExpr(
17531767
1754 // This could be a pointer or value depending on `unwrap_op`.1768 // This could be a pointer or value depending on `unwrap_op`.
1755 const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);1769 const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);
1770 const else_result = switch (rl) {
1771 .ref => unwrapped_payload,
1772 else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload),
1773 };
17561774
1757 return finishThenElseBlock(1775 return finishThenElseBlock(
1758 mod,1776 mod,
...@@ -1766,7 +1784,7 @@ fn orelseCatchExpr(...@@ -1766,7 +1784,7 @@ fn orelseCatchExpr(
1766 src,1784 src,
1767 src,1785 src,
1768 then_result,1786 then_result,
1769 unwrapped_payload,1787 else_result,
1770 block,1788 block,
1771 block,1789 block,
1772 );1790 );
...@@ -3955,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy {...@@ -3955,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy {
3955 }3973 }
3956}3974}
39573975
3976/// If the input ResultLoc is ref, returns ResultLoc.ref. Otherwise:
3977/// Returns ResultLoc.ty, where the type is determined by the input
3978/// ResultLoc type, wrapped in an optional type. If the input ResultLoc
3979/// has no type, .none is returned.
3980fn makeOptionalTypeResultLoc(mod: *Module, scope: *Scope, src: usize, rl: ResultLoc) !ResultLoc {
3981 switch (rl) {
3982 .ref => return ResultLoc.ref,
3983 .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => return ResultLoc.none,
3984 .ty => |elem_ty| {
3985 const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type, elem_ty);
3986 return ResultLoc{ .ty = wrapped_ty };
3987 },
3988 .ptr => |ptr_ty| {
3989 const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type_from_ptr_elem, ptr_ty);
3990 return ResultLoc{ .ty = wrapped_ty };
3991 },
3992 }
3993}
3994
3958fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void {3995fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void {
3959 // Depending on whether the result location is a pointer or value, different3996 // Depending on whether the result location is a pointer or value, different
3960 // ZIR needs to be generated. In the former case we rely on storing to the3997 // ZIR needs to be generated. In the former case we rely on storing to the
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 {
src/link.zig+2-2
...@@ -550,11 +550,11 @@ pub const File = struct {...@@ -550,11 +550,11 @@ pub const File = struct {
550 id_symlink_basename,550 id_symlink_basename,
551 &prev_digest_buf,551 &prev_digest_buf,
552 ) catch |err| b: {552 ) catch |err| b: {
553 log.debug("archive new_digest={x} readFile error: {s}", .{ digest, @errorName(err) });553 log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
554 break :b prev_digest_buf[0..0];554 break :b prev_digest_buf[0..0];
555 };555 };
556 if (mem.eql(u8, prev_digest, &digest)) {556 if (mem.eql(u8, prev_digest, &digest)) {
557 log.debug("archive digest={x} match - skipping invocation", .{digest});557 log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
558 base.lock = man.toOwnedLock();558 base.lock = man.toOwnedLock();
559 return;559 return;
560 }560 }
src/link/Coff.zig+3-3
...@@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {...@@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
892 id_symlink_basename,892 id_symlink_basename,
893 &prev_digest_buf,893 &prev_digest_buf,
894 ) catch |err| blk: {894 ) catch |err| blk: {
895 log.debug("COFF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });895 log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
896 // Handle this as a cache miss.896 // Handle this as a cache miss.
897 break :blk prev_digest_buf[0..0];897 break :blk prev_digest_buf[0..0];
898 };898 };
899 if (mem.eql(u8, prev_digest, &digest)) {899 if (mem.eql(u8, prev_digest, &digest)) {
900 log.debug("COFF LLD digest={x} match - skipping invocation", .{digest});900 log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
901 // Hot diggity dog! The output binary is already there.901 // Hot diggity dog! The output binary is already there.
902 self.base.lock = man.toOwnedLock();902 self.base.lock = man.toOwnedLock();
903 return;903 return;
904 }904 }
905 log.debug("COFF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });905 log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
906906
907 // We are about to change the output file to be different, so we invalidate the build hash now.907 // We are about to change the output file to be different, so we invalidate the build hash now.
908 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {908 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/link/Elf.zig+3-3
...@@ -1365,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {...@@ -1365,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
1365 id_symlink_basename,1365 id_symlink_basename,
1366 &prev_digest_buf,1366 &prev_digest_buf,
1367 ) catch |err| blk: {1367 ) catch |err| blk: {
1368 log.debug("ELF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });1368 log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
1369 // Handle this as a cache miss.1369 // Handle this as a cache miss.
1370 break :blk prev_digest_buf[0..0];1370 break :blk prev_digest_buf[0..0];
1371 };1371 };
1372 if (mem.eql(u8, prev_digest, &digest)) {1372 if (mem.eql(u8, prev_digest, &digest)) {
1373 log.debug("ELF LLD digest={x} match - skipping invocation", .{digest});1373 log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
1374 // Hot diggity dog! The output binary is already there.1374 // Hot diggity dog! The output binary is already there.
1375 self.base.lock = man.toOwnedLock();1375 self.base.lock = man.toOwnedLock();
1376 return;1376 return;
1377 }1377 }
1378 log.debug("ELF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });1378 log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
13791379
1380 // We are about to change the output file to be different, so we invalidate the build hash now.1380 // We are about to change the output file to be different, so we invalidate the build hash now.
1381 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {1381 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/link/MachO.zig+3-3
...@@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
556 id_symlink_basename,556 id_symlink_basename,
557 &prev_digest_buf,557 &prev_digest_buf,
558 ) catch |err| blk: {558 ) catch |err| blk: {
559 log.debug("MachO LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });559 log.debug("MachO LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
560 // Handle this as a cache miss.560 // Handle this as a cache miss.
561 break :blk prev_digest_buf[0..0];561 break :blk prev_digest_buf[0..0];
562 };562 };
563 if (mem.eql(u8, prev_digest, &digest)) {563 if (mem.eql(u8, prev_digest, &digest)) {
564 log.debug("MachO LLD digest={x} match - skipping invocation", .{digest});564 log.debug("MachO LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
565 // Hot diggity dog! The output binary is already there.565 // Hot diggity dog! The output binary is already there.
566 self.base.lock = man.toOwnedLock();566 self.base.lock = man.toOwnedLock();
567 return;567 return;
568 }568 }
569 log.debug("MachO LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });569 log.debug("MachO LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
570570
571 // We are about to change the output file to be different, so we invalidate the build hash now.571 // We are about to change the output file to be different, so we invalidate the build hash now.
572 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {572 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/link/Wasm.zig+3-3
...@@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {...@@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
391 id_symlink_basename,391 id_symlink_basename,
392 &prev_digest_buf,392 &prev_digest_buf,
393 ) catch |err| blk: {393 ) catch |err| blk: {
394 log.debug("WASM LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });394 log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
395 // Handle this as a cache miss.395 // Handle this as a cache miss.
396 break :blk prev_digest_buf[0..0];396 break :blk prev_digest_buf[0..0];
397 };397 };
398 if (mem.eql(u8, prev_digest, &digest)) {398 if (mem.eql(u8, prev_digest, &digest)) {
399 log.debug("WASM LLD digest={x} match - skipping invocation", .{digest});399 log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
400 // Hot diggity dog! The output binary is already there.400 // Hot diggity dog! The output binary is already there.
401 self.base.lock = man.toOwnedLock();401 self.base.lock = man.toOwnedLock();
402 return;402 return;
403 }403 }
404 log.debug("WASM LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });404 log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
405405
406 // We are about to change the output file to be different, so we invalidate the build hash now.406 // We are about to change the output file to be different, so we invalidate the build hash now.
407 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {407 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/zir.zig+5
...@@ -299,6 +299,9 @@ pub const Inst = struct {...@@ -299,6 +299,9 @@ pub const Inst = struct {
299 xor,299 xor,
300 /// Create an optional type '?T'300 /// Create an optional type '?T'
301 optional_type,301 optional_type,
302 /// Create an optional type '?T'. The operand is a pointer value. The optional type will
303 /// be the type of the pointer element, wrapped in an optional.
304 optional_type_from_ptr_elem,
302 /// Create a union type.305 /// Create a union type.
303 union_type,306 union_type,
304 /// ?T => T with safety.307 /// ?T => T with safety.
...@@ -397,6 +400,7 @@ pub const Inst = struct {...@@ -397,6 +400,7 @@ pub const Inst = struct {
397 .mut_slice_type,400 .mut_slice_type,
398 .const_slice_type,401 .const_slice_type,
399 .optional_type,402 .optional_type,
403 .optional_type_from_ptr_elem,
400 .optional_payload_safe,404 .optional_payload_safe,
401 .optional_payload_unsafe,405 .optional_payload_unsafe,
402 .optional_payload_safe_ptr,406 .optional_payload_safe_ptr,
...@@ -597,6 +601,7 @@ pub const Inst = struct {...@@ -597,6 +601,7 @@ pub const Inst = struct {
597 .typeof,601 .typeof,
598 .xor,602 .xor,
599 .optional_type,603 .optional_type,
604 .optional_type_from_ptr_elem,
600 .optional_payload_safe,605 .optional_payload_safe,
601 .optional_payload_unsafe,606 .optional_payload_unsafe,
602 .optional_payload_safe_ptr,607 .optional_payload_safe_ptr,
src/zir_sema.zig+11
...@@ -131,6 +131,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -131,6 +131,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
131 .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?),131 .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?),
132 .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?),132 .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?),
133 .optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?),133 .optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
134 .optional_type_from_ptr_elem => return zirOptionalTypeFromPtrElem(mod, scope, old_inst.castTag(.optional_type_from_ptr_elem).?),
134 .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),135 .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),
135 .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),136 .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),
136 .optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true),137 .optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true),
...@@ -1093,6 +1094,16 @@ fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerE...@@ -1093,6 +1094,16 @@ fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerE
1093 return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));1094 return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));
1094}1095}
10951096
1097fn zirOptionalTypeFromPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1098 const tracy = trace(@src());
1099 defer tracy.end();
1100
1101 const ptr = try resolveInst(mod, scope, inst.positionals.operand);
1102 const elem_ty = ptr.ty.elemType();
1103
1104 return mod.constType(scope, inst.base.src, try mod.optionalType(scope, elem_ty));
1105}
1106
1096fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {1107fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {
1097 const tracy = trace(@src());1108 const tracy = trace(@src());
1098 defer tracy.end();1109 defer tracy.end();
test/stage2/llvm.zig+68
...@@ -132,4 +132,72 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -132,4 +132,72 @@ 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 \\ var val3_var = opt_val orelse 30;
161 \\
162 \\ assert(val1 == 10);
163 \\ assert(val1_1 == 10);
164 \\ assert(ptr_val1.* == 10);
165 \\ assert(ptr_val1_1.* == 10);
166 \\
167 \\ assert(val2 == 20);
168 \\ assert(val2_2 == 20);
169 \\ assert(ptr_val2.* == 20);
170 \\
171 \\ assert(val3 == 10);
172 \\ assert(val3_var == 10);
173 \\
174 \\ (null_val orelse val2) = 1234;
175 \\ assert(val2 == 1234);
176 \\
177 \\ (opt_val orelse val2) = 5678;
178 \\ assert(opt_val.? == 5678);
179 \\
180 \\ return 0;
181 \\}
182 , "");
183 }
184
185 {
186 var case = ctx.exeUsingLlvmBackend("for loop", linux_x64);
187
188 case.addCompareOutput(
189 \\fn assert(ok: bool) void {
190 \\ if (!ok) unreachable;
191 \\}
192 \\
193 \\export fn main() c_int {
194 \\ var x: u32 = 0;
195 \\ for ("hello") |_| {
196 \\ x += 1;
197 \\ }
198 \\ assert("hello".len == x);
199 \\ return 0;
200 \\}
201 , "");
202 }
135}203}