| author | |
| committer | |
| log | 4b57fb5f23aa6513ceaebe396d1faf215cf0a475 |
| tree | a6060a67d81b1f875e0cfb947fadec9325b37a89 |
| parent | 3ad9cb8b473820ec5ea11d85aa72e8ddc83cfa03 |
| parent | 713f1138222dc40355c34c70d83b0a0805bd46c6 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
stage2: add support for optionals in the LLVM backend11 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 | 453 | return rvalue(mod, scope, rl, result); |
| 454 | 454 | }, |
| 455 | 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 | 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 | 474 | .block_two, .block_two_semicolon => { |
| 465 | 475 | const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs }; |
| ... | ... | @@ -1699,9 +1709,13 @@ fn orelseCatchExpr( |
| 1699 | 1709 | setBlockResultLoc(&block_scope, rl); |
| 1700 | 1710 | defer block_scope.instructions.deinit(mod.gpa); |
| 1701 | 1711 | |
| 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 | 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 | 1719 | const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand); |
| 1706 | 1720 | |
| 1707 | 1721 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ |
| ... | ... | @@ -1753,6 +1767,10 @@ fn orelseCatchExpr( |
| 1753 | 1767 | |
| 1754 | 1768 | // This could be a pointer or value depending on `unwrap_op`. |
| 1755 | 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 | }; | |
| 1756 | 1774 | |
| 1757 | 1775 | return finishThenElseBlock( |
| 1758 | 1776 | mod, |
| ... | ... | @@ -1766,7 +1784,7 @@ fn orelseCatchExpr( |
| 1766 | 1784 | src, |
| 1767 | 1785 | src, |
| 1768 | 1786 | then_result, |
| 1769 | unwrapped_payload, | |
| 1787 | else_result, | |
| 1770 | 1788 | block, |
| 1771 | 1789 | block, |
| 1772 | 1790 | ); |
| ... | ... | @@ -3955,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy { |
| 3955 | 3973 | } |
| 3956 | 3974 | } |
| 3957 | 3975 | |
| 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. | |
| 3980 | fn 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 | ||
| 3958 | 3995 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { |
| 3959 | 3996 | // Depending on whether the result location is a pointer or value, different |
| 3960 | 3997 | // 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 | 397 | .block => try self.genBlock(inst.castTag(.block).?), |
| 398 | 398 | .br => try self.genBr(inst.castTag(.br).?), |
| 399 | 399 | .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), |
| 400 | .br_void => try self.genBrVoid(inst.castTag(.br_void).?), | |
| 400 | 401 | .call => try self.genCall(inst.castTag(.call).?), |
| 401 | 402 | .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq), |
| 402 | 403 | .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt), |
| ... | ... | @@ -406,6 +407,10 @@ pub const LLVMIRModule = struct { |
| 406 | 407 | .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq), |
| 407 | 408 | .condbr => try self.genCondBr(inst.castTag(.condbr).?), |
| 408 | 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 | 414 | .load => try self.genLoad(inst.castTag(.load).?), |
| 410 | 415 | .loop => try self.genLoop(inst.castTag(.loop).?), |
| 411 | 416 | .not => try self.genNot(inst.castTag(.not).?), |
| ... | ... | @@ -414,6 +419,8 @@ pub const LLVMIRModule = struct { |
| 414 | 419 | .store => try self.genStore(inst.castTag(.store).?), |
| 415 | 420 | .sub => try self.genSub(inst.castTag(.sub).?), |
| 416 | 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 | 424 | .dbg_stmt => blk: { |
| 418 | 425 | // TODO: implement debug info |
| 419 | 426 | break :blk null; |
| ... | ... | @@ -534,21 +541,29 @@ pub const LLVMIRModule = struct { |
| 534 | 541 | } |
| 535 | 542 | |
| 536 | 543 | fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { |
| 537 | // Get the block that we want to break to. | |
| 538 | 544 | var block = self.blocks.get(inst.block).?; |
| 539 | _ = self.builder.buildBr(block.parent_bb); | |
| 540 | 545 | |
| 541 | 546 | // If the break doesn't break a value, then we don't have to add |
| 542 | 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); | |
| 544 | 553 | |
| 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); | |
| 548 | 558 | |
| 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 | } | |
| 551 | 563 | |
| 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 | 567 | return null; |
| 553 | 568 | } |
| 554 | 569 | |
| ... | ... | @@ -591,6 +606,44 @@ pub const LLVMIRModule = struct { |
| 591 | 606 | return null; |
| 592 | 607 | } |
| 593 | 608 | |
| 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 | 647 | fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { |
| 595 | 648 | const lhs = try self.resolveInst(inst.lhs); |
| 596 | 649 | const rhs = try self.resolveInst(inst.rhs); |
| ... | ... | @@ -751,6 +804,13 @@ pub const LLVMIRModule = struct { |
| 751 | 804 | // TODO: consider using buildInBoundsGEP2 for opaque pointers |
| 752 | 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 | 814 | else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), |
| 755 | 815 | }, |
| 756 | 816 | .Array => { |
| ... | ... | @@ -765,6 +825,29 @@ pub const LLVMIRModule = struct { |
| 765 | 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 | 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 | 873 | const elem_type = try self.getLLVMType(t.elemType(), src); |
| 791 | 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 | 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 | 21 | pub const voidType = LLVMVoidTypeInContext; |
| 22 | 22 | extern fn LLVMVoidTypeInContext(C: *const Context) *const Type; |
| 23 | 23 | |
| 24 | pub const structType = LLVMStructTypeInContext; | |
| 25 | extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type; | |
| 26 | ||
| 24 | 27 | pub const constString = LLVMConstStringInContext; |
| 25 | 28 | extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; |
| 26 | 29 | |
| 30 | pub const constStruct = LLVMConstStructInContext; | |
| 31 | extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value; | |
| 32 | ||
| 27 | 33 | pub const createBasicBlock = LLVMCreateBasicBlockInContext; |
| 28 | 34 | extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; |
| 29 | 35 | |
| ... | ... | @@ -204,6 +210,9 @@ pub const Builder = opaque { |
| 204 | 210 | |
| 205 | 211 | pub const buildPhi = LLVMBuildPhi; |
| 206 | 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 | }; |
| 208 | 217 | |
| 209 | 218 | pub const IntPredicate = extern enum { |
src/link.zig+2-2| ... | ... | @@ -550,11 +550,11 @@ pub const File = struct { |
| 550 | 550 | id_symlink_basename, |
| 551 | 551 | &prev_digest_buf, |
| 552 | 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 | 554 | break :b prev_digest_buf[0..0]; |
| 555 | 555 | }; |
| 556 | 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 | 558 | base.lock = man.toOwnedLock(); |
| 559 | 559 | return; |
| 560 | 560 | } |
src/link/Coff.zig+3-3| ... | ... | @@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { |
| 892 | 892 | id_symlink_basename, |
| 893 | 893 | &prev_digest_buf, |
| 894 | 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 | 896 | // Handle this as a cache miss. |
| 897 | 897 | break :blk prev_digest_buf[0..0]; |
| 898 | 898 | }; |
| 899 | 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 | 901 | // Hot diggity dog! The output binary is already there. |
| 902 | 902 | self.base.lock = man.toOwnedLock(); |
| 903 | 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) }); | |
| 906 | 906 | |
| 907 | 907 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 908 | 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 | 1365 | id_symlink_basename, |
| 1366 | 1366 | &prev_digest_buf, |
| 1367 | 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 | 1369 | // Handle this as a cache miss. |
| 1370 | 1370 | break :blk prev_digest_buf[0..0]; |
| 1371 | 1371 | }; |
| 1372 | 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 | 1374 | // Hot diggity dog! The output binary is already there. |
| 1375 | 1375 | self.base.lock = man.toOwnedLock(); |
| 1376 | 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) }); | |
| 1379 | 1379 | |
| 1380 | 1380 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 1381 | 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 | 556 | id_symlink_basename, |
| 557 | 557 | &prev_digest_buf, |
| 558 | 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 | 560 | // Handle this as a cache miss. |
| 561 | 561 | break :blk prev_digest_buf[0..0]; |
| 562 | 562 | }; |
| 563 | 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 | 565 | // Hot diggity dog! The output binary is already there. |
| 566 | 566 | self.base.lock = man.toOwnedLock(); |
| 567 | 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) }); | |
| 570 | 570 | |
| 571 | 571 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 572 | 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 | 391 | id_symlink_basename, |
| 392 | 392 | &prev_digest_buf, |
| 393 | 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 | 395 | // Handle this as a cache miss. |
| 396 | 396 | break :blk prev_digest_buf[0..0]; |
| 397 | 397 | }; |
| 398 | 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 | 400 | // Hot diggity dog! The output binary is already there. |
| 401 | 401 | self.base.lock = man.toOwnedLock(); |
| 402 | 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) }); | |
| 405 | 405 | |
| 406 | 406 | // We are about to change the output file to be different, so we invalidate the build hash now. |
| 407 | 407 | directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { |
src/zir.zig+5| ... | ... | @@ -299,6 +299,9 @@ pub const Inst = struct { |
| 299 | 299 | xor, |
| 300 | 300 | /// Create an optional type '?T' |
| 301 | 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 | 305 | /// Create a union type. |
| 303 | 306 | union_type, |
| 304 | 307 | /// ?T => T with safety. |
| ... | ... | @@ -397,6 +400,7 @@ pub const Inst = struct { |
| 397 | 400 | .mut_slice_type, |
| 398 | 401 | .const_slice_type, |
| 399 | 402 | .optional_type, |
| 403 | .optional_type_from_ptr_elem, | |
| 400 | 404 | .optional_payload_safe, |
| 401 | 405 | .optional_payload_unsafe, |
| 402 | 406 | .optional_payload_safe_ptr, |
| ... | ... | @@ -597,6 +601,7 @@ pub const Inst = struct { |
| 597 | 601 | .typeof, |
| 598 | 602 | .xor, |
| 599 | 603 | .optional_type, |
| 604 | .optional_type_from_ptr_elem, | |
| 600 | 605 | .optional_payload_safe, |
| 601 | 606 | .optional_payload_unsafe, |
| 602 | 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 | 131 | .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?), |
| 132 | 132 | .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?), |
| 133 | 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 | 135 | .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true), |
| 135 | 136 | .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false), |
| 136 | 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 | 1094 | return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type)); |
| 1094 | 1095 | } |
| 1095 | 1096 | |
| 1097 | fn 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 | ||
| 1096 | 1107 | fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { |
| 1097 | 1108 | const tracy = trace(@src()); |
| 1098 | 1109 | defer tracy.end(); |
test/stage2/llvm.zig+68| ... | ... | @@ -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 | } |