authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-05-12 09:13:20-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-05-17 02:08:41-04:00
log025611629f95cda21ff93a517406ea8387ad9cfb
treeae0a5a16e5780b5c20111a0ec2059eb4c3fe8ea2
parent6d68a494c8a5abf86c406a82c0f744a999084abe

x86_64: implement `@memmove`


3 files changed, 107 insertions(+), 12 deletions(-)

src/arch/x86_64/CodeGen.zig+103-7
...@@ -101399,8 +101399,66 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {...@@ -101399,8 +101399,66 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
101399101399
101400 .memset => try cg.airMemset(inst, false),101400 .memset => try cg.airMemset(inst, false),
101401 .memset_safe => try cg.airMemset(inst, true),101401 .memset_safe => try cg.airMemset(inst, true),
101402 .memcpy => try cg.airMemcpy(inst),101402 .memcpy, .memmove => |air_tag| if (use_old) switch (air_tag) {
101403 .memmove => try cg.airMemmove(inst),101403 else => unreachable,
101404 .memcpy => try cg.airMemcpy(inst),
101405 .memmove => return cg.fail("TODO implement airMemmove for {}", .{cg.target.cpu.arch}),
101406 } else {
101407 const bin_op = air_datas[@intFromEnum(inst)].bin_op;
101408 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }) ++ .{undefined};
101409 ops[2] = ops[0].getByteLen(cg) catch |err| switch (err) {
101410 error.SelectFailed => return cg.fail("failed to select {s} {} {} {} {}", .{
101411 @tagName(air_tag),
101412 cg.typeOf(bin_op.lhs).fmt(pt),
101413 cg.typeOf(bin_op.rhs).fmt(pt),
101414 ops[0].tracking(cg),
101415 ops[1].tracking(cg),
101416 }),
101417 else => |e| return e,
101418 };
101419 try ops[0].toSlicePtr(cg);
101420 cg.select(&.{}, &.{}, &ops, switch (air_tag) {
101421 else => unreachable,
101422 inline .memcpy, .memmove => |symbol| comptime &.{.{
101423 .patterns = &.{
101424 .{ .src = .{
101425 .{ .to_param_gpr = .{ .cc = .ccc, .index = 0 } },
101426 .{ .to_param_gpr = .{ .cc = .ccc, .index = 1 } },
101427 .{ .to_param_gpr = .{ .cc = .ccc, .index = 2 } },
101428 } },
101429 },
101430 .call_frame = .{ .alignment = .@"16" },
101431 .extra_temps = .{
101432 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = @tagName(symbol) } } },
101433 .unused,
101434 .unused,
101435 .unused,
101436 .unused,
101437 .unused,
101438 .unused,
101439 .unused,
101440 .unused,
101441 .unused,
101442 .unused,
101443 },
101444 .clobbers = .{ .eflags = true, .caller_preserved = .ccc },
101445 .each = .{ .once = &.{
101446 .{ ._, ._, .call, .tmp0d, ._, ._, ._ },
101447 } },
101448 }},
101449 }) catch |err| switch (err) {
101450 error.SelectFailed => return cg.fail("failed to select {s} {} {} {} {} {}", .{
101451 @tagName(air_tag),
101452 cg.typeOf(bin_op.lhs).fmt(pt),
101453 cg.typeOf(bin_op.rhs).fmt(pt),
101454 ops[0].tracking(cg),
101455 ops[1].tracking(cg),
101456 ops[2].tracking(cg),
101457 }),
101458 else => |e| return e,
101459 };
101460 for (ops) |op| try op.die(cg);
101461 },
101404 .cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst),101462 .cmpxchg_weak, .cmpxchg_strong => try cg.airCmpxchg(inst),
101405 .atomic_load => try cg.airAtomicLoad(inst),101463 .atomic_load => try cg.airAtomicLoad(inst),
101406 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),101464 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered),
...@@ -118458,11 +118516,6 @@ fn airMemcpy(self: *CodeGen, inst: Air.Inst.Index) !void {...@@ -118458,11 +118516,6 @@ fn airMemcpy(self: *CodeGen, inst: Air.Inst.Index) !void {
118458 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });118516 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
118459}118517}
118460118518
118461fn airMemmove(self: *CodeGen, inst: Air.Inst.Index) !void {
118462 _ = inst;
118463 return self.fail("TODO implement airMemmove for {}", .{self.target.cpu.arch});
118464}
118465
118466fn airTagName(self: *CodeGen, inst: Air.Inst.Index, only_safety: bool) !void {118519fn airTagName(self: *CodeGen, inst: Air.Inst.Index, only_safety: bool) !void {
118467 const pt = self.pt;118520 const pt = self.pt;
118468 const zcu = pt.zcu;118521 const zcu = pt.zcu;
...@@ -122137,6 +122190,49 @@ const Temp = struct {...@@ -122137,6 +122190,49 @@ const Temp = struct {
122137 return .{ .index = new_temp_index.toIndex() };122190 return .{ .index = new_temp_index.toIndex() };
122138 }122191 }
122139122192
122193 fn getByteLen(temp: *Temp, cg: *CodeGen) Select.Error!Temp {
122194 const zcu = cg.pt.zcu;
122195 const ip = &zcu.intern_pool;
122196 const ptr_info = ip.indexToKey(temp.typeOf(cg).toIntern()).ptr_type;
122197 switch (ptr_info.flags.size) {
122198 .one => {
122199 const array_info = ip.indexToKey(ptr_info.child).array_type;
122200 return cg.tempInit(.usize, .{
122201 .immediate = Type.fromInterned(array_info.child).abiSize(zcu) * array_info.len,
122202 });
122203 },
122204 .many, .c => unreachable,
122205 .slice => {
122206 const elem_size = Type.fromInterned(ptr_info.child).abiSize(zcu);
122207 var len = try temp.getLimb(.usize, 1, cg);
122208 while (try len.toRegClass(true, .general_purpose, cg)) {}
122209 const len_reg = len.tracking(cg).short.register.to64();
122210 if (!std.math.isPowerOfTwo(elem_size)) {
122211 try cg.spillEflagsIfOccupied();
122212 try cg.asmRegisterRegisterImmediate(
122213 .{ .i_, .mul },
122214 len_reg,
122215 len_reg,
122216 .u(elem_size),
122217 );
122218 } else if (elem_size > 8) {
122219 try cg.spillEflagsIfOccupied();
122220 try cg.asmRegisterImmediate(
122221 .{ ._l, .sh },
122222 len_reg,
122223 .u(std.math.log2_int(u64, elem_size)),
122224 );
122225 } else if (elem_size != 1) try cg.asmRegisterMemory(.{ ._, .lea }, len_reg, .{
122226 .mod = .{ .rm = .{
122227 .index = len_reg,
122228 .scale = .fromFactor(@intCast(elem_size)),
122229 } },
122230 });
122231 return len;
122232 },
122233 }
122234 }
122235
122140 fn toLimb(temp: *Temp, limb_ty: Type, limb_index: u28, cg: *CodeGen) InnerError!void {122236 fn toLimb(temp: *Temp, limb_ty: Type, limb_index: u28, cg: *CodeGen) InnerError!void {
122141 switch (temp.unwrap(cg)) {122237 switch (temp.unwrap(cg)) {
122142 .ref => {},122238 .ref => {},
test/behavior/builtin_functions_returning_void_or_noreturn.zig-1
...@@ -6,7 +6,6 @@ var x: u8 = 1;...@@ -6,7 +6,6 @@ var x: u8 = 1;
66
7// This excludes builtin functions that return void or noreturn that cannot be tested.7// This excludes builtin functions that return void or noreturn that cannot be tested.
8test {8test {
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO11 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/memmove.zig+4-4
...@@ -3,13 +3,13 @@ const builtin = @import("builtin");...@@ -3,13 +3,13 @@ const builtin = @import("builtin");
3const expect = std.testing.expect;3const expect = std.testing.expect;
44
5test "memmove and memset intrinsics" {5test "memmove and memset intrinsics" {
6 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;9 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;10 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
12 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;11 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
12 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1313
14 try testMemmoveMemset();14 try testMemmoveMemset();
15 try comptime testMemmoveMemset();15 try comptime testMemmoveMemset();
...@@ -33,13 +33,13 @@ fn testMemmoveMemset() !void {...@@ -33,13 +33,13 @@ fn testMemmoveMemset() !void {
33}33}
3434
35test "@memmove with both operands single-ptr-to-array, one is null-terminated" {35test "@memmove with both operands single-ptr-to-array, one is null-terminated" {
36 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
37 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;36 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
38 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;37 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
39 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;38 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
40 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;39 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
41 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;40 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
42 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;41 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
42 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
4343
44 try testMemmoveBothSinglePtrArrayOneIsNullTerminated();44 try testMemmoveBothSinglePtrArrayOneIsNullTerminated();
45 try comptime testMemmoveBothSinglePtrArrayOneIsNullTerminated();45 try comptime testMemmoveBothSinglePtrArrayOneIsNullTerminated();
...@@ -79,13 +79,13 @@ fn testMemmoveBothSinglePtrArrayOneIsNullTerminated() !void {...@@ -79,13 +79,13 @@ fn testMemmoveBothSinglePtrArrayOneIsNullTerminated() !void {
79}79}
8080
81test "@memmove dest many pointer" {81test "@memmove dest many pointer" {
82 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
83 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;82 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
84 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;83 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
85 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;84 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
86 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;85 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;86 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
88 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;87 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
88 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
8989
90 try testMemmoveDestManyPtr();90 try testMemmoveDestManyPtr();
91 try comptime testMemmoveDestManyPtr();91 try comptime testMemmoveDestManyPtr();
...@@ -123,13 +123,13 @@ fn testMemmoveDestManyPtr() !void {...@@ -123,13 +123,13 @@ fn testMemmoveDestManyPtr() !void {
123}123}
124124
125test "@memmove slice" {125test "@memmove slice" {
126 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
127 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;126 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
128 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;127 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
129 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;128 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
130 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;129 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
131 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;130 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
132 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;131 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
132 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
133133
134 try testMemmoveSlice();134 try testMemmoveSlice();
135 try comptime testMemmoveSlice();135 try comptime testMemmoveSlice();