| 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,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); |
| 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 | 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); |
| 1706 | 1720 | ||
| 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( |
| 1753 | 1767 | ||
| 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 | }; | ||
| 1756 | 1774 | ||
| 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 | } |
| 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 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { | 3995 | fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void { |
| 3959 | // Depending on whether the result location is a pointer or value, different | 3996 | // 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 the | 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,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 info | 425 | // 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 | } |
| 535 | 542 | ||
| 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); | ||
| 540 | 545 | ||
| 541 | // If the break doesn't break a value, then we don't have to add | 546 | // 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); | ||
| 544 | 553 | ||
| 545 | // For the phi node, we need the basic blocks and the values of the | 554 | // 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); | ||
| 548 | 558 | ||
| 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 | } | ||
| 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 | return null; | 567 | return null; |
| 553 | } | 568 | } |
| 554 | 569 | ||
| ... | @@ -591,6 +606,44 @@ pub const LLVMIRModule = struct { | ... | @@ -591,6 +606,44 @@ pub const LLVMIRModule = struct { |
| 591 | return null; | 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 | 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 pointers | 804 | // 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; |
| 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 | 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; |
| 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 | 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; |
| 29 | 35 | ||
| ... | @@ -204,6 +210,9 @@ pub const Builder = opaque { | ... | @@ -204,6 +210,9 @@ pub const Builder = opaque { |
| 204 | 210 | ||
| 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 | }; |
| 208 | 217 | ||
| 209 | pub const IntPredicate = extern enum { | 218 | pub 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) }); |
| 906 | 906 | ||
| 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) }); |
| 1379 | 1379 | ||
| 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) }); |
| 570 | 570 | ||
| 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) }); |
| 405 | 405 | ||
| 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 | } |
| 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 | fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { | 1107 | fn 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 | } |