authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-20 21:45:11-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-20 21:45:11-07:00
loga0e195120dd3dd7919fd249a6ce2201da9395898
tree1f06851929602eb29932c60418e774f0cb1cab03
parent3b2e25ed8718d8aee085497886967fa21a9697cc

stage2: implement slicing

* New AIR instruction: slice, which constructs a slice out of a pointer and a length. * AstGen: use `coerced_ty` for start and end expressions, use `none` for the sentinel, and don't try to load the result of the slice operation because it returns a by-value result. * Sema: pointer arithmetic is extracted into analyzePointerArithmetic and it is used by the implementation of slice. - Also I implemented comptime pointer addition. * Sema: extract logic into analyzeSlicePtr, analyzeSliceLen and use them inside the slice semantic analysis. - The approach in stage2 is much cleaner than stage1 because it uses more granular analysis calls for obtaining the slice pointer, doing arithmetic on it, and checking if the length is comptime-known. * Sema: use the slice Value Tag for slices when doing coercion from pointer-to-array. * LLVM backend: detect when emitting a GEP instruction into a pointer-to-array and add the extra index that is required. * Type: ptrAlignment for c_void returns 0. * Implement Value.hash and Value.eql for slices. * Remove accidentally duplicated behavior test.

16 files changed, 383 insertions(+), 226 deletions(-)

src/Air.zig+4
......@@ -360,6 +360,9 @@ pub const Inst = struct {
360360 /// Given a tagged union value, get its tag value.
361361 /// Uses the `ty_op` field.
362362 get_union_tag,
363 /// Constructs a slice from a pointer and a length.
364 /// Uses the `ty_pl` field, payload is `Bin`. lhs is ptr, rhs is len.
365 slice,
363366 /// Given a slice value, return the length.
364367 /// Result type is always usize.
365368 /// Uses the `ty_op` field.
......@@ -694,6 +697,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
694697 .ptr_elem_ptr,
695698 .cmpxchg_weak,
696699 .cmpxchg_strong,
700 .slice,
697701 => return air.getRefType(datas[inst].ty_pl.ty),
698702
699703 .not,
src/AstGen.zig+9-27
......@@ -727,56 +727,38 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
727727
728728 .slice_open => {
729729 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
730 const start = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs);
730 const start = try expr(gz, scope, .{ .coerced_ty = .usize_type }, node_datas[node].rhs);
731731 const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
732732 .lhs = lhs,
733733 .start = start,
734734 });
735 switch (rl) {
736 .ref => return result,
737 else => {
738 const dereffed = try gz.addUnNode(.load, result, node);
739 return rvalue(gz, rl, dereffed, node);
740 },
741 }
735 return rvalue(gz, rl, result, node);
742736 },
743737 .slice => {
744738 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
745739 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
746 const start = try expr(gz, scope, .{ .ty = .usize_type }, extra.start);
747 const end = try expr(gz, scope, .{ .ty = .usize_type }, extra.end);
740 const start = try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.start);
741 const end = try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.end);
748742 const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
749743 .lhs = lhs,
750744 .start = start,
751745 .end = end,
752746 });
753 switch (rl) {
754 .ref => return result,
755 else => {
756 const dereffed = try gz.addUnNode(.load, result, node);
757 return rvalue(gz, rl, dereffed, node);
758 },
759 }
747 return rvalue(gz, rl, result, node);
760748 },
761749 .slice_sentinel => {
762750 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
763751 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
764 const start = try expr(gz, scope, .{ .ty = .usize_type }, extra.start);
765 const end = if (extra.end != 0) try expr(gz, scope, .{ .ty = .usize_type }, extra.end) else .none;
766 const sentinel = try expr(gz, scope, .{ .ty = .usize_type }, extra.sentinel);
752 const start = try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.start);
753 const end = if (extra.end != 0) try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.end) else .none;
754 const sentinel = try expr(gz, scope, .none, extra.sentinel);
767755 const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
768756 .lhs = lhs,
769757 .start = start,
770758 .end = end,
771759 .sentinel = sentinel,
772760 });
773 switch (rl) {
774 .ref => return result,
775 else => {
776 const dereffed = try gz.addUnNode(.load, result, node);
777 return rvalue(gz, rl, dereffed, node);
778 },
779 }
761 return rvalue(gz, rl, result, node);
780762 },
781763
782764 .deref => {
src/Liveness.zig+1
......@@ -266,6 +266,7 @@ fn analyzeInst(
266266 .set_union_tag,
267267 .min,
268268 .max,
269 .slice,
269270 => {
270271 const o = inst_datas[inst].bin_op;
271272 return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
src/Sema.zig+164-92
......@@ -7083,7 +7083,6 @@ fn analyzeArithmetic(
70837083 if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize()) {
70847084 .One, .Slice => {},
70857085 .Many, .C => {
7086 // Pointer arithmetic.
70877086 const op_src = src; // TODO better source location
70887087 const air_tag: Air.Inst.Tag = switch (zir_tag) {
70897088 .add => .ptr_add,
......@@ -7095,24 +7094,7 @@ fn analyzeArithmetic(
70957094 .{@tagName(zir_tag)},
70967095 ),
70977096 };
7098 // TODO if the operand is comptime-known to be negative, or is a negative int,
7099 // coerce to isize instead of usize.
7100 const casted_rhs = try sema.coerce(block, Type.usize, rhs, rhs_src);
7101 const runtime_src = runtime_src: {
7102 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
7103 if (try sema.resolveDefinedValue(block, rhs_src, casted_rhs)) |rhs_val| {
7104 _ = lhs_val;
7105 _ = rhs_val;
7106 return sema.fail(block, src, "TODO implement Sema for comptime pointer arithmetic", .{});
7107 } else {
7108 break :runtime_src rhs_src;
7109 }
7110 } else {
7111 break :runtime_src lhs_src;
7112 }
7113 };
7114 try sema.requireRuntimeBlock(block, runtime_src);
7115 return block.addBinOp(air_tag, lhs, casted_rhs);
7097 return analyzePtrArithmetic(sema, block, op_src, lhs, rhs, air_tag, lhs_src, rhs_src);
71167098 },
71177099 };
71187100
......@@ -7716,6 +7698,38 @@ fn analyzeArithmetic(
77167698 return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
77177699}
77187700
7701fn analyzePtrArithmetic(
7702 sema: *Sema,
7703 block: *Block,
7704 op_src: LazySrcLoc,
7705 ptr: Air.Inst.Ref,
7706 uncasted_offset: Air.Inst.Ref,
7707 air_tag: Air.Inst.Tag,
7708 ptr_src: LazySrcLoc,
7709 offset_src: LazySrcLoc,
7710) CompileError!Air.Inst.Ref {
7711 // TODO if the operand is comptime-known to be negative, or is a negative int,
7712 // coerce to isize instead of usize.
7713 const offset = try sema.coerce(block, Type.usize, uncasted_offset, offset_src);
7714 // TODO adjust the return type according to alignment and other factors
7715 const runtime_src = rs: {
7716 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
7717 if (try sema.resolveDefinedValue(block, offset_src, offset)) |offset_val| {
7718 if (air_tag == .ptr_sub) {
7719 return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});
7720 }
7721 const offset_int = offset_val.toUnsignedInt();
7722 const new_ptr_val = try ptr_val.elemPtr(sema.arena, offset_int);
7723 const new_ptr_ty = sema.typeOf(ptr);
7724 return sema.addConstant(new_ptr_ty, new_ptr_val);
7725 } else break :rs offset_src;
7726 } else break :rs ptr_src;
7727 };
7728
7729 try sema.requireRuntimeBlock(block, runtime_src);
7730 return block.addBinOp(air_tag, ptr, offset);
7731}
7732
77197733fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
77207734 const tracy = trace(@src());
77217735 defer tracy.end();
......@@ -10820,33 +10834,13 @@ fn fieldVal(
1082010834 try sema.analyzeLoad(block, src, object, object_src)
1082110835 else
1082210836 object;
10823
10824 const buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
10825 const result_ty = inner_ty.slicePtrFieldType(buf);
10826
10827 if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| {
10828 if (val.isUndef()) return sema.addConstUndef(result_ty);
10829 return sema.addConstant(result_ty, val.slicePtr());
10830 }
10831 try sema.requireRuntimeBlock(block, src);
10832 return block.addTyOp(.slice_ptr, result_ty, slice);
10837 return sema.analyzeSlicePtr(block, src, slice, inner_ty, object_src);
1083310838 } else if (mem.eql(u8, field_name, "len")) {
1083410839 const slice = if (is_pointer_to)
1083510840 try sema.analyzeLoad(block, src, object, object_src)
1083610841 else
1083710842 object;
10838
10839 const result_ty = Type.usize;
10840
10841 if (try sema.resolveMaybeUndefVal(block, object_src, slice)) |val| {
10842 if (val.isUndef()) return sema.addConstUndef(result_ty);
10843 return sema.addConstant(
10844 result_ty,
10845 try Value.Tag.int_u64.create(arena, val.sliceLen()),
10846 );
10847 }
10848 try sema.requireRuntimeBlock(block, src);
10849 return block.addTyOp(.slice_len, result_ty, slice);
10843 return sema.analyzeSliceLen(block, src, slice);
1085010844 } else {
1085110845 return sema.fail(
1085210846 block,
......@@ -12349,8 +12343,13 @@ fn coerceArrayPtrToSlice(
1234912343 inst_src: LazySrcLoc,
1235012344) CompileError!Air.Inst.Ref {
1235112345 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
12352 // The comptime Value representation is compatible with both types.
12353 return sema.addConstant(dest_ty, val);
12346 const ptr_array_ty = sema.typeOf(inst);
12347 const array_ty = ptr_array_ty.childType();
12348 const slice_val = try Value.Tag.slice.create(sema.arena, .{
12349 .ptr = val,
12350 .len = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()),
12351 });
12352 return sema.addConstant(dest_ty, slice_val);
1235412353 }
1235512354 try sema.requireRuntimeBlock(block, inst_src);
1235612355 return block.addTyOp(.array_to_slice, dest_ty, inst);
......@@ -12632,6 +12631,25 @@ fn analyzeLoad(
1263212631 return block.addTyOp(.load, elem_ty, ptr);
1263312632}
1263412633
12634fn analyzeSlicePtr(
12635 sema: *Sema,
12636 block: *Block,
12637 src: LazySrcLoc,
12638 slice: Air.Inst.Ref,
12639 slice_ty: Type,
12640 slice_src: LazySrcLoc,
12641) CompileError!Air.Inst.Ref {
12642 const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer);
12643 const result_ty = slice_ty.slicePtrFieldType(buf);
12644
12645 if (try sema.resolveMaybeUndefVal(block, slice_src, slice)) |val| {
12646 if (val.isUndef()) return sema.addConstUndef(result_ty);
12647 return sema.addConstant(result_ty, val.slicePtr());
12648 }
12649 try sema.requireRuntimeBlock(block, src);
12650 return block.addTyOp(.slice_ptr, result_ty, slice);
12651}
12652
1263512653fn analyzeSliceLen(
1263612654 sema: *Sema,
1263712655 block: *Block,
......@@ -12703,74 +12721,128 @@ fn analyzeSlice(
1270312721 sema: *Sema,
1270412722 block: *Block,
1270512723 src: LazySrcLoc,
12706 array_ptr: Air.Inst.Ref,
12707 start: Air.Inst.Ref,
12708 end_opt: Air.Inst.Ref,
12724 ptr_ptr: Air.Inst.Ref,
12725 uncasted_start: Air.Inst.Ref,
12726 uncasted_end_opt: Air.Inst.Ref,
1270912727 sentinel_opt: Air.Inst.Ref,
1271012728 sentinel_src: LazySrcLoc,
1271112729) CompileError!Air.Inst.Ref {
12712 const array_ptr_ty = sema.typeOf(array_ptr);
12713 const ptr_child = switch (array_ptr_ty.zigTypeTag()) {
12714 .Pointer => array_ptr_ty.elemType(),
12715 else => return sema.fail(block, src, "expected pointer, found '{}'", .{array_ptr_ty}),
12730 const ptr_src = src; // TODO better source location
12731 const start_src = src; // TODO better source location
12732 const end_src = src; // TODO better source location
12733 // Slice expressions can operate on a variable whose type is an array. This requires
12734 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
12735 const ptr_ptr_ty = sema.typeOf(ptr_ptr);
12736 const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag()) {
12737 .Pointer => ptr_ptr_ty.elemType(),
12738 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty}),
1271612739 };
1271712740
12718 var array_type = ptr_child;
12719 const elem_type = switch (ptr_child.zigTypeTag()) {
12720 .Array => ptr_child.elemType(),
12721 .Pointer => blk: {
12722 if (ptr_child.isSinglePointer()) {
12723 if (ptr_child.elemType().zigTypeTag() == .Array) {
12724 array_type = ptr_child.elemType();
12725 break :blk ptr_child.elemType().elemType();
12741 var array_ty = ptr_ptr_child_ty;
12742 var slice_ty = ptr_ptr_ty;
12743 var ptr_or_slice = ptr_ptr;
12744 var elem_ty = ptr_ptr_child_ty.childType();
12745 switch (ptr_ptr_child_ty.zigTypeTag()) {
12746 .Array => {},
12747 .Pointer => {
12748 if (ptr_ptr_child_ty.isSinglePointer()) {
12749 const double_child_ty = ptr_ptr_child_ty.childType();
12750 if (double_child_ty.zigTypeTag() == .Array) {
12751 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
12752 slice_ty = ptr_ptr_child_ty;
12753 array_ty = double_child_ty;
12754 elem_ty = double_child_ty.childType();
12755 } else {
12756 return sema.fail(block, ptr_src, "slice of single-item pointer", .{});
1272612757 }
12727
12728 return sema.fail(block, src, "slice of single-item pointer", .{});
1272912758 }
12730 break :blk ptr_child.elemType();
1273112759 },
12732 else => return sema.fail(block, src, "slice of non-array type '{}'", .{ptr_child}),
12760 else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty}),
12761 }
12762 const ptr = if (slice_ty.isSlice())
12763 try sema.analyzeSlicePtr(block, src, ptr_or_slice, slice_ty, ptr_src)
12764 else
12765 ptr_or_slice;
12766
12767 const start = try sema.coerce(block, Type.usize, uncasted_start, start_src);
12768 const new_ptr = try analyzePtrArithmetic(sema, block, src, ptr, start, .ptr_add, ptr_src, start_src);
12769
12770 const end = e: {
12771 if (uncasted_end_opt != .none) {
12772 break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
12773 }
12774
12775 if (array_ty.zigTypeTag() == .Array) {
12776 break :e try sema.addConstant(
12777 Type.usize,
12778 try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()),
12779 );
12780 } else if (slice_ty.isSlice()) {
12781 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
12782 }
12783 return sema.fail(block, end_src, "slice of pointer must include end value", .{});
1273312784 };
1273412785
1273512786 const slice_sentinel = if (sentinel_opt != .none) blk: {
12736 const casted = try sema.coerce(block, elem_type, sentinel_opt, sentinel_src);
12787 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
1273712788 break :blk try sema.resolveConstValue(block, sentinel_src, casted);
1273812789 } else null;
1273912790
12740 var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice;
12741 var return_elem_type = elem_type;
12742 if (end_opt != .none) {
12743 if (try sema.resolveDefinedValue(block, src, end_opt)) |end_val| {
12744 if (try sema.resolveDefinedValue(block, src, start)) |start_val| {
12745 const start_u64 = start_val.toUnsignedInt();
12746 const end_u64 = end_val.toUnsignedInt();
12747 if (start_u64 > end_u64) {
12748 return sema.fail(block, src, "out of bounds slice", .{});
12749 }
12791 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
1275012792
12751 const len = end_u64 - start_u64;
12752 const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen())
12753 array_type.sentinel()
12754 else
12755 slice_sentinel;
12756 return_elem_type = try Type.array(sema.arena, len, array_sentinel, elem_type);
12757 return_ptr_size = .One;
12758 }
12793 const opt_new_ptr_val = try sema.resolveDefinedValue(block, ptr_src, new_ptr);
12794 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
12795
12796 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
12797
12798 if (opt_new_len_val) |new_len_val| {
12799 const new_len_int = new_len_val.toUnsignedInt();
12800
12801 const sentinel = if (array_ty.zigTypeTag() == .Array and new_len_int == array_ty.arrayLen())
12802 array_ty.sentinel()
12803 else
12804 slice_sentinel;
12805
12806 const return_ty = try Type.ptr(sema.arena, .{
12807 .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty),
12808 .sentinel = null,
12809 .@"align" = new_ptr_ty_info.@"align",
12810 .@"addrspace" = new_ptr_ty_info.@"addrspace",
12811 .mutable = new_ptr_ty_info.mutable,
12812 .@"allowzero" = new_ptr_ty_info.@"allowzero",
12813 .@"volatile" = new_ptr_ty_info.@"volatile",
12814 .size = .One,
12815 });
12816
12817 if (opt_new_ptr_val) |new_ptr_val| {
12818 return sema.addConstant(return_ty, new_ptr_val);
12819 } else {
12820 return block.addTyOp(.bitcast, return_ty, new_ptr);
1275912821 }
1276012822 }
12761 const return_type = try Type.ptr(sema.arena, .{
12762 .pointee_type = return_elem_type,
12763 .sentinel = if (end_opt == .none) slice_sentinel else null,
12764 .@"align" = 0, // TODO alignment
12765 .@"addrspace" = if (ptr_child.zigTypeTag() == .Pointer) ptr_child.ptrAddressSpace() else .generic,
12766 .mutable = !ptr_child.isConstPtr(),
12767 .@"allowzero" = ptr_child.isAllowzeroPtr(),
12768 .@"volatile" = ptr_child.isVolatilePtr(),
12769 .size = return_ptr_size,
12823
12824 const return_ty = try Type.ptr(sema.arena, .{
12825 .pointee_type = elem_ty,
12826 .sentinel = slice_sentinel,
12827 .@"align" = new_ptr_ty_info.@"align",
12828 .@"addrspace" = new_ptr_ty_info.@"addrspace",
12829 .mutable = new_ptr_ty_info.mutable,
12830 .@"allowzero" = new_ptr_ty_info.@"allowzero",
12831 .@"volatile" = new_ptr_ty_info.@"volatile",
12832 .size = .Slice,
1277012833 });
12771 _ = return_type;
1277212834
12773 return sema.fail(block, src, "TODO implement analysis of slice", .{});
12835 try sema.requireRuntimeBlock(block, src);
12836 return block.addInst(.{
12837 .tag = .slice,
12838 .data = .{ .ty_pl = .{
12839 .ty = try sema.addType(return_ty),
12840 .payload = try sema.addExtra(Air.Bin{
12841 .lhs = new_ptr,
12842 .rhs = new_len,
12843 }),
12844 } },
12845 });
1277412846}
1277512847
1277612848/// Asserts that lhs and rhs types are both numeric.
src/Zir.zig+1
......@@ -482,6 +482,7 @@ pub const Inst = struct {
482482 /// Includes a token source location.
483483 /// Uses the `un_tok` union field.
484484 /// The operand needs to get coerced to the function's return type.
485 /// TODO rename this to `ret_tok` because coercion is now done unconditionally in Sema.
485486 ret_coerce,
486487 /// Sends control flow back to the function's callee.
487488 /// The return operand is `error.foo` where `foo` is given by the string.
src/arch/aarch64/CodeGen.zig+7
......@@ -417,6 +417,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
417417 .shl_sat => try self.airShlSat(inst),
418418 .min => try self.airMin(inst),
419419 .max => try self.airMax(inst),
420 .slice => try self.airSlice(inst),
420421
421422 .cmp_lt => try self.airCmp(inst, .lt),
422423 .cmp_lte => try self.airCmp(inst, .lte),
......@@ -874,6 +875,12 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void {
874875 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
875876}
876877
878fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
879 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
880 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch});
881 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
882}
883
877884fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
878885 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
879886 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch});
src/codegen.zig+9
......@@ -765,6 +765,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
765765 .shl_sat => try self.airShlSat(inst),
766766 .min => try self.airMin(inst),
767767 .max => try self.airMax(inst),
768 .slice => try self.airSlice(inst),
768769
769770 .cmp_lt => try self.airCmp(inst, .lt),
770771 .cmp_lte => try self.airCmp(inst, .lte),
......@@ -1244,6 +1245,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12441245 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
12451246 }
12461247
1248 fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1249 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1250 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1251 else => return self.fail("TODO implement slice for {}", .{self.target.cpu.arch}),
1252 };
1253 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1254 }
1255
12471256 fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
12481257 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
12491258 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
src/codegen/c.zig+23-2
......@@ -992,6 +992,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
992992 .min => try airMinMax(f, inst, "<"),
993993 .max => try airMinMax(f, inst, ">"),
994994
995 .slice => try airSlice(f, inst),
996
995997 .cmp_eq => try airBinOp(f, inst, " == "),
996998 .cmp_gt => try airBinOp(f, inst, " > "),
997999 .cmp_gte => try airBinOp(f, inst, " >= "),
......@@ -1104,8 +1106,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
11041106}
11051107
11061108fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {
1107 if (f.liveness.isUnused(inst))
1108 return CValue.none;
1109 if (f.liveness.isUnused(inst)) return CValue.none;
11091110
11101111 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
11111112 const operand = try f.resolveInst(ty_op.operand);
......@@ -1641,6 +1642,26 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValu
16411642 return local;
16421643}
16431644
1645fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
1646 if (f.liveness.isUnused(inst)) return CValue.none;
1647
1648 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1649 const ptr = try f.resolveInst(bin_op.lhs);
1650 const len = try f.resolveInst(bin_op.rhs);
1651
1652 const writer = f.object.writer();
1653 const inst_ty = f.air.typeOfIndex(inst);
1654 const local = try f.allocLocal(inst_ty, .Const);
1655
1656 try writer.writeAll(" = {");
1657 try f.writeCValue(writer, ptr);
1658 try writer.writeAll(", ");
1659 try f.writeCValue(writer, len);
1660 try writer.writeAll("};\n");
1661
1662 return local;
1663}
1664
16441665fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
16451666 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
16461667 const extra = f.air.extraData(Air.Call, pl_op.payload);
src/codegen/llvm.zig+55-15
......@@ -1091,11 +1091,22 @@ pub const DeclGen = struct {
10911091 const elem_ptr = tv.val.castTag(.elem_ptr).?.data;
10921092 const parent_ptr = try self.lowerParentPtr(elem_ptr.array_ptr);
10931093 const llvm_usize = try self.llvmType(Type.usize);
1094 const indices: [2]*const llvm.Value = .{
1095 llvm_usize.constInt(0, .False),
1096 llvm_usize.constInt(elem_ptr.index, .False),
1097 };
1098 return parent_ptr.constInBoundsGEP(&indices, indices.len);
1094 if (parent_ptr.typeOf().getElementType().getTypeKind() == .Array) {
1095 const indices: [2]*const llvm.Value = .{
1096 llvm_usize.constInt(0, .False),
1097 llvm_usize.constInt(elem_ptr.index, .False),
1098 };
1099 return parent_ptr.constInBoundsGEP(&indices, indices.len);
1100 } else {
1101 const indices: [1]*const llvm.Value = .{
1102 llvm_usize.constInt(elem_ptr.index, .False),
1103 };
1104 return parent_ptr.constInBoundsGEP(&indices, indices.len);
1105 }
1106 },
1107 .null_value => {
1108 const llvm_type = try self.llvmType(tv.ty);
1109 return llvm_type.constNull();
10991110 },
11001111 else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }),
11011112 },
......@@ -1666,6 +1677,7 @@ pub const FuncGen = struct {
16661677 .shl_exact => try self.airShlExact(inst),
16671678 .min => try self.airMin(inst),
16681679 .max => try self.airMax(inst),
1680 .slice => try self.airSlice(inst),
16691681
16701682 .bit_and, .bool_and => try self.airAnd(inst),
16711683 .bit_or, .bool_or => try self.airOr(inst),
......@@ -2124,8 +2136,7 @@ pub const FuncGen = struct {
21242136 }
21252137
21262138 fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*const llvm.Value {
2127 if (self.liveness.isUnused(inst))
2128 return null;
2139 if (self.liveness.isUnused(inst)) return null;
21292140
21302141 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
21312142 const operand = try self.resolveInst(ty_op.operand);
......@@ -2721,6 +2732,19 @@ pub const FuncGen = struct {
27212732 return self.builder.buildUMax(lhs, rhs, "");
27222733 }
27232734
2735 fn airSlice(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2736 if (self.liveness.isUnused(inst)) return null;
2737
2738 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2739 const ptr = try self.resolveInst(bin_op.lhs);
2740 const len = try self.resolveInst(bin_op.rhs);
2741 const inst_ty = self.air.typeOfIndex(inst);
2742 const llvm_slice_ty = try self.dg.llvmType(inst_ty);
2743
2744 const partial = self.builder.buildInsertValue(llvm_slice_ty.getUndef(), ptr, 0, "");
2745 return self.builder.buildInsertValue(partial, len, 1, "");
2746 }
2747
27242748 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
27252749 if (self.liveness.isUnused(inst)) return null;
27262750
......@@ -2886,26 +2910,42 @@ pub const FuncGen = struct {
28862910 }
28872911
28882912 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2889 if (self.liveness.isUnused(inst))
2890 return null;
2913 if (self.liveness.isUnused(inst)) return null;
28912914
28922915 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
28932916 const base_ptr = try self.resolveInst(bin_op.lhs);
28942917 const offset = try self.resolveInst(bin_op.rhs);
2895 const indices: [1]*const llvm.Value = .{offset};
2896 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
2918 const ptr_ty = self.air.typeOf(bin_op.lhs);
2919 if (ptr_ty.ptrSize() == .One) {
2920 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
2921 const indices: [2]*const llvm.Value = .{
2922 self.context.intType(32).constNull(), offset,
2923 };
2924 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
2925 } else {
2926 const indices: [1]*const llvm.Value = .{offset};
2927 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
2928 }
28972929 }
28982930
28992931 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2900 if (self.liveness.isUnused(inst))
2901 return null;
2932 if (self.liveness.isUnused(inst)) return null;
29022933
29032934 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
29042935 const base_ptr = try self.resolveInst(bin_op.lhs);
29052936 const offset = try self.resolveInst(bin_op.rhs);
29062937 const negative_offset = self.builder.buildNeg(offset, "");
2907 const indices: [1]*const llvm.Value = .{negative_offset};
2908 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
2938 const ptr_ty = self.air.typeOf(bin_op.lhs);
2939 if (ptr_ty.ptrSize() == .One) {
2940 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
2941 const indices: [2]*const llvm.Value = .{
2942 self.context.intType(32).constNull(), negative_offset,
2943 };
2944 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
2945 } else {
2946 const indices: [1]*const llvm.Value = .{negative_offset};
2947 return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
2948 }
29092949 }
29102950
29112951 fn airAnd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
src/codegen/llvm/bindings.zig+3
......@@ -234,6 +234,9 @@ pub const Type = opaque {
234234
235235 pub const getTypeKind = LLVMGetTypeKind;
236236 extern fn LLVMGetTypeKind(Ty: *const Type) TypeKind;
237
238 pub const getElementType = LLVMGetElementType;
239 extern fn LLVMGetElementType(Ty: *const Type) *const Type;
237240};
238241
239242pub const Module = opaque {
src/print_air.zig+1
......@@ -140,6 +140,7 @@ const Writer = struct {
140140 .set_union_tag,
141141 .min,
142142 .max,
143 .slice,
143144 => try w.writeBinOp(s, inst),
144145
145146 .is_null,
src/type.zig+2-1
......@@ -1529,6 +1529,7 @@ pub const Type = extern union {
15291529 return fast_result;
15301530 }
15311531
1532 /// Returns 0 if the pointer is naturally aligned and the element type is 0-bit.
15321533 pub fn ptrAlignment(self: Type, target: Target) u32 {
15331534 switch (self.tag()) {
15341535 .single_const_pointer,
......@@ -1739,10 +1740,10 @@ pub const Type = extern union {
17391740
17401741 .empty_struct,
17411742 .void,
1743 .c_void,
17421744 => return 0,
17431745
17441746 .empty_struct_literal,
1745 .c_void,
17461747 .type,
17471748 .comptime_int,
17481749 .comptime_float,
src/value.zig+19
......@@ -761,6 +761,7 @@ pub const Value = extern union {
761761 return decl_val.toAllocatedBytes(decl.ty, allocator);
762762 },
763763 .the_only_possible_value => return &[_]u8{},
764 .slice => return toAllocatedBytes(val.castTag(.slice).?.data.ptr, ty, allocator),
764765 else => unreachable,
765766 }
766767 }
......@@ -1402,6 +1403,16 @@ pub const Value = extern union {
14021403 var buffer: Type.Payload.ElemType = undefined;
14031404 return eql(a_payload, b_payload, ty.optionalChild(&buffer));
14041405 },
1406 .slice => {
1407 const a_payload = a.castTag(.slice).?.data;
1408 const b_payload = b.castTag(.slice).?.data;
1409 if (!eql(a_payload.len, b_payload.len, Type.usize)) return false;
1410
1411 var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
1412 const ptr_ty = ty.slicePtrFieldType(&ptr_buf);
1413
1414 return eql(a_payload.ptr, b_payload.ptr, ptr_ty);
1415 },
14051416 .elem_ptr => @panic("TODO: Implement more pointer eql cases"),
14061417 .field_ptr => @panic("TODO: Implement more pointer eql cases"),
14071418 .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
......@@ -1475,6 +1486,14 @@ pub const Value = extern union {
14751486 .variable,
14761487 => std.hash.autoHash(hasher, val.pointerDecl().?),
14771488
1489 .slice => {
1490 const slice = val.castTag(.slice).?.data;
1491 var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
1492 const ptr_ty = ty.slicePtrFieldType(&ptr_buf);
1493 hash(slice.ptr, ptr_ty, hasher);
1494 hash(slice.len, Type.usize, hasher);
1495 },
1496
14781497 .elem_ptr => @panic("TODO: Implement more pointer hashing cases"),
14791498 .field_ptr => @panic("TODO: Implement more pointer hashing cases"),
14801499 .eu_payload_ptr => @panic("TODO: Implement more pointer hashing cases"),
test/behavior/eval.zig-4
......@@ -395,10 +395,6 @@ test "f32 at compile time is lossy" {
395395 try expect(@as(f32, 1 << 24) + 1 == 1 << 24);
396396}
397397
398test "f32 at compile time is lossy" {
399 try expect(@as(f32, 1 << 24) + 1 == 1 << 24);
400}
401
402398test "f64 at compile time is lossy" {
403399 try expect(@as(f64, 1 << 53) + 1 == 1 << 53);
404400}
test/behavior/slice.zig+85
......@@ -24,3 +24,88 @@ comptime {
2424 var pos = S.indexOfScalar(type, list, c_ulong).?;
2525 if (pos != 1) @compileError("bad pos");
2626}
27
28test "slicing" {
29 var array: [20]i32 = undefined;
30
31 array[5] = 1234;
32
33 var slice = array[5..10];
34
35 if (slice.len != 5) unreachable;
36
37 const ptr = &slice[0];
38 if (ptr.* != 1234) unreachable;
39
40 var slice_rest = array[10..];
41 if (slice_rest.len != 10) unreachable;
42}
43
44test "const slice" {
45 comptime {
46 const a = "1234567890";
47 try expect(a.len == 10);
48 const b = a[1..2];
49 try expect(b.len == 1);
50 try expect(b[0] == '2');
51 }
52}
53
54test "comptime slice of undefined pointer of length 0" {
55 const slice1 = @as([*]i32, undefined)[0..0];
56 try expect(slice1.len == 0);
57 const slice2 = @as([*]i32, undefined)[100..100];
58 try expect(slice2.len == 0);
59}
60
61test "implicitly cast array of size 0 to slice" {
62 var msg = [_]u8{};
63 try assertLenIsZero(&msg);
64}
65
66fn assertLenIsZero(msg: []const u8) !void {
67 try expect(msg.len == 0);
68}
69
70test "access len index of sentinel-terminated slice" {
71 const S = struct {
72 fn doTheTest() !void {
73 var slice: [:0]const u8 = "hello";
74
75 try expect(slice.len == 5);
76 try expect(slice[5] == 0);
77 }
78 };
79 try S.doTheTest();
80 comptime try S.doTheTest();
81}
82
83test "comptime slice of slice preserves comptime var" {
84 comptime {
85 var buff: [10]u8 = undefined;
86 buff[0..][0..][0] = 1;
87 try expect(buff[0..][0..][0] == 1);
88 }
89}
90
91test "slice of type" {
92 comptime {
93 var types_array = [_]type{ i32, f64, type };
94 for (types_array) |T, i| {
95 switch (i) {
96 0 => try expect(T == i32),
97 1 => try expect(T == f64),
98 2 => try expect(T == type),
99 else => unreachable,
100 }
101 }
102 for (types_array[0..]) |T, i| {
103 switch (i) {
104 0 => try expect(T == i32),
105 1 => try expect(T == f64),
106 2 => try expect(T == type),
107 else => unreachable,
108 }
109 }
110 }
111}
test/behavior/slice_stage1.zig-85
......@@ -4,39 +4,6 @@ const expectEqualSlices = std.testing.expectEqualSlices;
44const expectEqual = std.testing.expectEqual;
55const mem = std.mem;
66
7test "slicing" {
8 var array: [20]i32 = undefined;
9
10 array[5] = 1234;
11
12 var slice = array[5..10];
13
14 if (slice.len != 5) unreachable;
15
16 const ptr = &slice[0];
17 if (ptr.* != 1234) unreachable;
18
19 var slice_rest = array[10..];
20 if (slice_rest.len != 10) unreachable;
21}
22
23test "const slice" {
24 comptime {
25 const a = "1234567890";
26 try expect(a.len == 10);
27 const b = a[1..2];
28 try expect(b.len == 1);
29 try expect(b[0] == '2');
30 }
31}
32
33test "comptime slice of undefined pointer of length 0" {
34 const slice1 = @as([*]i32, undefined)[0..0];
35 try expect(slice1.len == 0);
36 const slice2 = @as([*]i32, undefined)[100..100];
37 try expect(slice2.len == 0);
38}
39
407test "slicing zero length array" {
418 const s1 = ""[0..];
429 const s2 = ([_]u32{})[0..];
......@@ -97,15 +64,6 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 {
9764 return a_slice[start..end];
9865}
9966
100test "implicitly cast array of size 0 to slice" {
101 var msg = [_]u8{};
102 try assertLenIsZero(&msg);
103}
104
105fn assertLenIsZero(msg: []const u8) !void {
106 try expect(msg.len == 0);
107}
108
10967test "C pointer" {
11068 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";
11169 var len: u32 = 10;
......@@ -150,19 +108,6 @@ test "slice type with custom alignment" {
150108 try expect(array[1].anything == 42);
151109}
152110
153test "access len index of sentinel-terminated slice" {
154 const S = struct {
155 fn doTheTest() !void {
156 var slice: [:0]const u8 = "hello";
157
158 try expect(slice.len == 5);
159 try expect(slice[5] == 0);
160 }
161 };
162 try S.doTheTest();
163 comptime try S.doTheTest();
164}
165
166111test "obtaining a null terminated slice" {
167112 // here we have a normal array
168113 var buf: [50]u8 = undefined;
......@@ -407,14 +352,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" {
407352 comptime try S.doTheTest();
408353}
409354
410test "comptime slice of slice preserves comptime var" {
411 comptime {
412 var buff: [10]u8 = undefined;
413 buff[0..][0..][0] = 1;
414 try expect(buff[0..][0..][0] == 1);
415 }
416}
417
418355test "comptime slice of pointer preserves comptime var" {
419356 comptime {
420357 var buff: [10]u8 = undefined;
......@@ -433,28 +370,6 @@ test "array concat of slices gives slice" {
433370 }
434371}
435372
436test "slice of type" {
437 comptime {
438 var types_array = [_]type{ i32, f64, type };
439 for (types_array) |T, i| {
440 switch (i) {
441 0 => try expect(T == i32),
442 1 => try expect(T == f64),
443 2 => try expect(T == type),
444 else => unreachable,
445 }
446 }
447 for (types_array[0..]) |T, i| {
448 switch (i) {
449 0 => try expect(T == i32),
450 1 => try expect(T == f64),
451 2 => try expect(T == type),
452 else => unreachable,
453 }
454 }
455 }
456}
457
458373test "comptime pointer cast array and then slice" {
459374 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
460375