Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 75 additions & 39 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14562,8 +14562,9 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai

const mutable_alloc = try block.addTy(.alloc, alloc_ty);

// if both the source and destination are arrays
// we can hotpath via a memcpy.
// there's nothing to copy
if (result_len == 0 and res_sent_val == null) return mutable_alloc;

if (lhs_ty.zigTypeTag(zcu) == .pointer and
rhs_ty.zigTypeTag(zcu) == .pointer)
{
Expand All @@ -14576,48 +14577,83 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
});

const many_ty = slice_ty.slicePtrFieldType(zcu);
const many_alloc = try block.addBitCast(many_ty, mutable_alloc);
const many_ty_ref = Air.internedToRef(many_ty.toIntern());

// lhs_dest_slice = dest[0..lhs.len]
const slice_ty_ref = Air.internedToRef(slice_ty.toIntern());
const lhs_len_ref = try pt.intRef(.usize, lhs_len);
const lhs_dest_slice = try block.addInst(.{
.tag = .slice,
.data = .{ .ty_pl = .{
.ty = slice_ty_ref,
.payload = try sema.addExtra(Air.Bin{
.lhs = many_alloc,
.rhs = lhs_len_ref,
}),
} },
});

_ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs);
// @memcpy(@as(*[lhs.len]T, dst[0..lhs.len]), lhs.ptr)
if (lhs_len != 0) {
const lhs_dest_slice = switch (rhs_len) {
0 => mutable_alloc,
else => s: {
// [lhs.len]T
const array_ty = try pt.arrayType(.{
.child = resolved_elem_ty.toIntern(),
.len = lhs_len,
});
// *[lhs.len]T
const mutable_ty = try pt.ptrTypeSema(.{
.child = array_ty.toIntern(),
.flags = .{ .address_space = ptr_as },
});
break :s try block.addBitCast(mutable_ty, mutable_alloc);
},
};
const lhs_src_pointer = if (lhs_ty.isSlice(zcu))
try block.addInst(.{
.tag = .slice_ptr,
.data = .{ .ty_op = .{
.ty = many_ty_ref,
.operand = lhs,
} },
})
else
lhs;
_ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs_src_pointer);
}

if (rhs_len != 0) {
const rhs_dest_slice = switch (lhs_len) {
0 => mutable_alloc,
else => s: {
const many_alloc = try block.addBitCast(many_ty, mutable_alloc);
const rhs_dest_offset = try block.addInst(.{
.tag = .ptr_add,
.data = .{ .ty_pl = .{
.ty = many_ty_ref,
.payload = try sema.addExtra(Air.Bin{
.lhs = many_alloc,
.rhs = lhs_len_ref,
}),
} },
});

// rhs_dest_slice = dest[lhs.len..][0..rhs.len]
const rhs_len_ref = try pt.intRef(.usize, rhs_len);
const rhs_dest_offset = try block.addInst(.{
.tag = .ptr_add,
.data = .{ .ty_pl = .{
.ty = Air.internedToRef(many_ty.toIntern()),
.payload = try sema.addExtra(Air.Bin{
.lhs = many_alloc,
.rhs = lhs_len_ref,
}),
} },
});
const rhs_dest_slice = try block.addInst(.{
.tag = .slice,
.data = .{ .ty_pl = .{
.ty = slice_ty_ref,
.payload = try sema.addExtra(Air.Bin{
.lhs = rhs_dest_offset,
.rhs = rhs_len_ref,
}),
} },
});
// [rhs.len]T
const array_ty = try pt.arrayType(.{
.child = resolved_elem_ty.toIntern(),
.len = lhs_len,
});
// *[rhs.len]T
const mutable_ty = try pt.ptrTypeSema(.{
.child = array_ty.toIntern(),
.flags = .{ .address_space = ptr_as },
});

_ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs);
break :s try block.addBitCast(mutable_ty, rhs_dest_offset);
},
};
const rhs_src_pointer = if (rhs_ty.isSlice(zcu))
try block.addInst(.{
.tag = .slice_ptr,
.data = .{ .ty_op = .{
.ty = many_ty_ref,
.operand = rhs,
} },
})
else
rhs;
_ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs_src_pointer);
}

if (res_sent_val) |sent_val| {
const elem_index = try pt.intRef(.usize, result_len);
Expand Down
6 changes: 6 additions & 0 deletions test/behavior/array.zig
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ test "array init with concat" {
try expect(std.mem.eql(u8, &i, "abcd"));
}

test "array concat zero length pointers" {
var x: *const [0]u8 = &.{};
_ = &x;
_ = @as([]const u8, "") ++ x;
}

test "array init with mult" {
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
Expand Down
Loading