authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-12-01 23:27:35-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-12-03 10:22:06-05:00
log014833b61fae62d75935606ff2946009c708fd58
tree842d7f2e87256d8a11feab975998dbab265b7b05
parent917b4ad5e0b21d37e3b0f69dc0db02a62cf2e75b

x86_64: implement more compliant vectors


4 files changed, 214 insertions(+), 120 deletions(-)

src/arch/x86_64/CodeGen.zig+160-99
......@@ -5133,74 +5133,114 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
51335133 const mod = self.bin_file.options.module.?;
51345134 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
51355135
5136 const array_ty = self.typeOf(bin_op.lhs);
5137 const array = try self.resolveInst(bin_op.lhs);
5138 const array_lock: ?RegisterLock = switch (array) {
5139 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5140 else => null,
5141 };
5142 defer if (array_lock) |lock| self.register_manager.unlockReg(lock);
5136 const result: MCValue = result: {
5137 const array_ty = self.typeOf(bin_op.lhs);
5138 const elem_ty = array_ty.childType(mod);
51435139
5144 const elem_ty = array_ty.childType(mod);
5145 const elem_abi_size = elem_ty.abiSize(mod);
5140 const array_mcv = try self.resolveInst(bin_op.lhs);
5141 const array_lock: ?RegisterLock = switch (array_mcv) {
5142 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5143 else => null,
5144 };
5145 defer if (array_lock) |lock| self.register_manager.unlockReg(lock);
51465146
5147 const index_ty = self.typeOf(bin_op.rhs);
5148 const index = try self.resolveInst(bin_op.rhs);
5149 const index_lock: ?RegisterLock = switch (index) {
5150 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5151 else => null,
5152 };
5153 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
5147 const index_ty = self.typeOf(bin_op.rhs);
5148 const index_mcv = try self.resolveInst(bin_op.rhs);
5149 const index_lock: ?RegisterLock = switch (index_mcv) {
5150 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5151 else => null,
5152 };
5153 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
51545154
5155 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
5156 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
5157 defer self.register_manager.unlockReg(addr_lock);
5155 try self.spillEflagsIfOccupied();
5156 if (array_ty.isVector(mod) and elem_ty.toIntern() == .bool_type) {
5157 const index_reg = switch (index_mcv) {
5158 .register => |reg| reg,
5159 else => try self.copyToTmpRegister(index_ty, index_mcv),
5160 };
5161 switch (array_mcv) {
5162 .register => |array_reg| try self.asmRegisterRegister(
5163 .{ ._, .bt },
5164 array_reg.to64(),
5165 index_reg.to64(),
5166 ),
5167 .load_frame => try self.asmMemoryRegister(
5168 .{ ._, .bt },
5169 try array_mcv.mem(self, .qword),
5170 index_reg.to64(),
5171 ),
5172 .memory, .load_symbol, .load_direct, .load_got, .load_tlv => try self.asmMemoryRegister(
5173 .{ ._, .bt },
5174 .{
5175 .base = .{
5176 .reg = try self.copyToTmpRegister(Type.usize, array_mcv.address()),
5177 },
5178 .mod = .{ .rm = .{ .size = .qword } },
5179 },
5180 index_reg.to64(),
5181 ),
5182 else => return self.fail("TODO airArrayElemVal for {s} of {}", .{
5183 @tagName(array_mcv), array_ty.fmt(mod),
5184 }),
5185 }
51585186
5159 switch (array) {
5160 .register => {
5161 const frame_index = try self.allocFrameIndex(FrameAlloc.initType(array_ty, mod));
5162 try self.genSetMem(.{ .frame = frame_index }, 0, array_ty, array);
5163 try self.asmRegisterMemory(
5187 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp);
5188 try self.asmSetccRegister(.c, dst_reg.to8());
5189 break :result .{ .register = dst_reg };
5190 }
5191
5192 const elem_abi_size = elem_ty.abiSize(mod);
5193 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
5194 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
5195 defer self.register_manager.unlockReg(addr_lock);
5196
5197 switch (array_mcv) {
5198 .register => {
5199 const frame_index = try self.allocFrameIndex(FrameAlloc.initType(array_ty, mod));
5200 try self.genSetMem(.{ .frame = frame_index }, 0, array_ty, array_mcv);
5201 try self.asmRegisterMemory(
5202 .{ ._, .lea },
5203 addr_reg,
5204 .{ .base = .{ .frame = frame_index }, .mod = .{ .rm = .{ .size = .qword } } },
5205 );
5206 },
5207 .load_frame => |frame_addr| try self.asmRegisterMemory(
51645208 .{ ._, .lea },
51655209 addr_reg,
5166 .{ .base = .{ .frame = frame_index }, .mod = .{ .rm = .{ .size = .qword } } },
5167 );
5168 },
5169 .load_frame => |frame_addr| try self.asmRegisterMemory(
5170 .{ ._, .lea },
5171 addr_reg,
5172 .{
5173 .base = .{ .frame = frame_addr.index },
5174 .mod = .{ .rm = .{ .size = .qword, .disp = frame_addr.off } },
5175 },
5176 ),
5177 .memory,
5178 .load_symbol,
5179 .load_direct,
5180 .load_got,
5181 .load_tlv,
5182 => try self.genSetReg(addr_reg, Type.usize, array.address()),
5183 .lea_symbol, .lea_direct, .lea_tlv => unreachable,
5184 else => return self.fail("TODO implement array_elem_val when array is {}", .{array}),
5185 }
5186
5187 const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
5188 const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
5189 defer self.register_manager.unlockReg(offset_lock);
5210 .{
5211 .base = .{ .frame = frame_addr.index },
5212 .mod = .{ .rm = .{ .size = .qword, .disp = frame_addr.off } },
5213 },
5214 ),
5215 .memory,
5216 .load_symbol,
5217 .load_direct,
5218 .load_got,
5219 .load_tlv,
5220 => try self.genSetReg(addr_reg, Type.usize, array_mcv.address()),
5221 .lea_symbol, .lea_direct, .lea_tlv => unreachable,
5222 else => return self.fail("TODO airArrayElemVal_val for {s} of {}", .{
5223 @tagName(array_mcv), array_ty.fmt(mod),
5224 }),
5225 }
51905226
5191 // TODO we could allocate register here, but need to expect addr register and potentially
5192 // offset register.
5193 try self.spillEflagsIfOccupied();
5194 const dst_mcv = try self.allocRegOrMem(inst, false);
5195 try self.genBinOpMir(
5196 .{ ._, .add },
5197 Type.usize,
5198 .{ .register = addr_reg },
5199 .{ .register = offset_reg },
5200 );
5201 try self.genCopy(elem_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } });
5227 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size);
5228 const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
5229 defer self.register_manager.unlockReg(offset_lock);
52025230
5203 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
5231 // TODO we could allocate register here, but need to expect addr register and potentially
5232 // offset register.
5233 const dst_mcv = try self.allocRegOrMem(inst, false);
5234 try self.genBinOpMir(
5235 .{ ._, .add },
5236 Type.usize,
5237 .{ .register = addr_reg },
5238 .{ .register = offset_reg },
5239 );
5240 try self.genCopy(elem_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } });
5241 break :result dst_mcv;
5242 };
5243 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
52045244}
52055245
52065246fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5258,32 +5298,44 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
52585298 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
52595299 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
52605300
5261 const ptr_ty = self.typeOf(extra.lhs);
5262 const ptr = try self.resolveInst(extra.lhs);
5263 const ptr_lock: ?RegisterLock = switch (ptr) {
5264 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5265 else => null,
5266 };
5267 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
5301 const result = result: {
5302 const elem_ptr_ty = self.typeOfIndex(inst);
5303 const base_ptr_ty = self.typeOf(extra.lhs);
52685304
5269 const elem_ty = ptr_ty.elemType2(mod);
5270 const elem_abi_size = elem_ty.abiSize(mod);
5271 const index_ty = self.typeOf(extra.rhs);
5272 const index = try self.resolveInst(extra.rhs);
5273 const index_lock: ?RegisterLock = switch (index) {
5274 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5275 else => null,
5276 };
5277 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
5305 const base_ptr_mcv = try self.resolveInst(extra.lhs);
5306 const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) {
5307 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5308 else => null,
5309 };
5310 defer if (base_ptr_lock) |lock| self.register_manager.unlockReg(lock);
52785311
5279 const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
5280 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
5281 defer self.register_manager.unlockReg(offset_reg_lock);
5312 if (elem_ptr_ty.ptrInfo(mod).flags.vector_index != .none) {
5313 break :result if (self.reuseOperand(inst, extra.lhs, 0, base_ptr_mcv))
5314 base_ptr_mcv
5315 else
5316 try self.copyToRegisterWithInstTracking(inst, elem_ptr_ty, base_ptr_mcv);
5317 }
52825318
5283 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);
5284 try self.genBinOpMir(.{ ._, .add }, ptr_ty, dst_mcv, .{ .register = offset_reg });
5319 const elem_ty = base_ptr_ty.elemType2(mod);
5320 const elem_abi_size = elem_ty.abiSize(mod);
5321 const index_ty = self.typeOf(extra.rhs);
5322 const index_mcv = try self.resolveInst(extra.rhs);
5323 const index_lock: ?RegisterLock = switch (index_mcv) {
5324 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5325 else => null,
5326 };
5327 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
52855328
5286 return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none });
5329 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size);
5330 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
5331 defer self.register_manager.unlockReg(offset_reg_lock);
5332
5333 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, elem_ptr_ty, base_ptr_mcv);
5334 try self.genBinOpMir(.{ ._, .add }, elem_ptr_ty, dst_mcv, .{ .register = offset_reg });
5335
5336 break :result dst_mcv;
5337 };
5338 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
52875339}
52885340
52895341fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
......@@ -6712,19 +6764,20 @@ fn reuseOperandAdvanced(
67126764
67136765fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerError!void {
67146766 const mod = self.bin_file.options.module.?;
6715 const ptr_info = ptr_ty.ptrInfo(mod);
67166767
6768 const ptr_info = ptr_ty.ptrInfo(mod);
67176769 const val_ty = Type.fromInterned(ptr_info.child);
67186770 if (!val_ty.hasRuntimeBitsIgnoreComptime(mod)) return;
67196771 const val_abi_size: u32 = @intCast(val_ty.abiSize(mod));
67206772
6721 if (ptr_info.packed_offset.bit_offset % 8 == 0) {
6722 try self.load(
6723 dst_mcv,
6724 ptr_ty,
6725 ptr_mcv.offset(@intCast(@divExact(ptr_info.packed_offset.bit_offset, 8))),
6726 );
6727 const val_bit_size: u32 = @intCast(val_ty.bitSize(mod));
6773 const val_bit_size: u32 = @intCast(val_ty.bitSize(mod));
6774 const ptr_bit_off = ptr_info.packed_offset.bit_offset + switch (ptr_info.flags.vector_index) {
6775 .none => 0,
6776 .runtime => unreachable,
6777 else => |vector_index| @intFromEnum(vector_index) * val_bit_size,
6778 };
6779 if (ptr_bit_off % 8 == 0) {
6780 try self.load(dst_mcv, ptr_ty, ptr_mcv.offset(@intCast(@divExact(ptr_bit_off, 8))));
67286781 if (val_abi_size * 8 > val_bit_size) {
67296782 if (dst_mcv.isRegister()) {
67306783 try self.truncateRegister(val_ty, dst_mcv.getReg().?);
......@@ -6746,9 +6799,8 @@ fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) Inn
67466799
67476800 const limb_abi_size: u32 = @min(val_abi_size, 8);
67486801 const limb_abi_bits = limb_abi_size * 8;
6749 const val_byte_off: i32 =
6750 @intCast(ptr_info.packed_offset.bit_offset / limb_abi_bits * limb_abi_size);
6751 const val_bit_off = ptr_info.packed_offset.bit_offset % limb_abi_bits;
6802 const val_byte_off: i32 = @intCast(ptr_bit_off / limb_abi_bits * limb_abi_size);
6803 const val_bit_off = ptr_bit_off % limb_abi_bits;
67526804 const val_extra_bits = self.regExtraBits(val_ty);
67536805
67546806 const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv);
......@@ -6875,7 +6927,8 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
68756927 else
68766928 try self.allocRegOrMem(inst, true);
68776929
6878 if (ptr_ty.ptrInfo(mod).packed_offset.host_size > 0) {
6930 const ptr_info = ptr_ty.ptrInfo(mod);
6931 if (ptr_info.flags.vector_index != .none or ptr_info.packed_offset.host_size > 0) {
68796932 try self.packedLoad(dst_mcv, ptr_ty, ptr_mcv);
68806933 } else {
68816934 try self.load(dst_mcv, ptr_ty, ptr_mcv);
......@@ -6909,7 +6962,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
69096962fn packedStore(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerError!void {
69106963 const mod = self.bin_file.options.module.?;
69116964 const ptr_info = ptr_ty.ptrInfo(mod);
6912 const src_ty = ptr_ty.childType(mod);
6965 const src_ty = Type.fromInterned(ptr_info.child);
69136966 if (!src_ty.hasRuntimeBitsIgnoreComptime(mod)) return;
69146967
69156968 const limb_abi_size: u16 = @min(ptr_info.packed_offset.host_size, 8);
......@@ -6917,8 +6970,13 @@ fn packedStore(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) In
69176970 const limb_ty = try mod.intType(.unsigned, limb_abi_bits);
69186971
69196972 const src_bit_size = src_ty.bitSize(mod);
6920 const src_byte_off: i32 = @intCast(ptr_info.packed_offset.bit_offset / limb_abi_bits * limb_abi_size);
6921 const src_bit_off = ptr_info.packed_offset.bit_offset % limb_abi_bits;
6973 const ptr_bit_off = ptr_info.packed_offset.bit_offset + switch (ptr_info.flags.vector_index) {
6974 .none => 0,
6975 .runtime => unreachable,
6976 else => |vector_index| @intFromEnum(vector_index) * src_bit_size,
6977 };
6978 const src_byte_off: i32 = @intCast(ptr_bit_off / limb_abi_bits * limb_abi_size);
6979 const src_bit_off = ptr_bit_off % limb_abi_bits;
69226980
69236981 const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv);
69246982 const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg);
......@@ -7055,11 +7113,14 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
70557113 const ptr_mcv = try self.resolveInst(bin_op.lhs);
70567114 const ptr_ty = self.typeOf(bin_op.lhs);
70577115 const src_mcv = try self.resolveInst(bin_op.rhs);
7058 if (ptr_ty.ptrInfo(mod).packed_offset.host_size > 0) {
7116
7117 const ptr_info = ptr_ty.ptrInfo(mod);
7118 if (ptr_info.flags.vector_index != .none or ptr_info.packed_offset.host_size > 0) {
70597119 try self.packedStore(ptr_ty, ptr_mcv, src_mcv);
70607120 } else {
70617121 try self.store(ptr_ty, ptr_mcv, src_mcv);
70627122 }
7123
70637124 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
70647125}
70657126
......@@ -12777,7 +12838,7 @@ fn moveStrategy(self: *Self, ty: Type, class: Register.Class, aligned: bool) !Mo
1277712838 else => {
1277812839 const classes = mem.sliceTo(&abi.classifySystemV(ty, mod, .other), .none);
1277912840 assert(std.mem.indexOfNone(abi.Class, classes, &.{
12780 .integer, .sse, .float, .float_combine,
12841 .integer, .sse, .memory, .float, .float_combine,
1278112842 }) == null);
1278212843 const abi_size = ty.abiSize(mod);
1278312844 if (abi_size < 4 or
src/codegen.zig+54-19
......@@ -389,31 +389,66 @@ pub fn generateSymbol(
389389 },
390390 },
391391 .vector_type => |vector_type| {
392 switch (aggregate.storage) {
393 .bytes => |bytes| try code.appendSlice(bytes),
394 .elems, .repeated_elem => {
395 var index: u64 = 0;
396 while (index < vector_type.len) : (index += 1) {
397 switch (try generateSymbol(bin_file, src_loc, .{
398 .ty = Type.fromInterned(vector_type.child),
399 .val = Value.fromInterned(switch (aggregate.storage) {
400 .bytes => unreachable,
401 .elems => |elems| elems[@as(usize, @intCast(index))],
402 .repeated_elem => |elem| elem,
403 }),
404 }, code, debug_output, reloc_info)) {
405 .ok => {},
406 .fail => |em| return .{ .fail = em },
407 }
392 const abi_size = math.cast(usize, typed_value.ty.abiSize(mod)) orelse
393 return error.Overflow;
394 switch (vector_type.child) {
395 .bool_type => {
396 const bytes = try code.addManyAsSlice(abi_size);
397 @memset(bytes, 0xaa);
398 var index: usize = 0;
399 const len = math.cast(usize, vector_type.len) orelse return error.Overflow;
400 while (index < len) : (index += 1) {
401 const bit_index = switch (endian) {
402 .big => len - 1 - index,
403 .little => index,
404 };
405 const byte = &bytes[bit_index / 8];
406 const mask = @as(u8, 1) << @truncate(bit_index);
407 if (switch (switch (aggregate.storage) {
408 .bytes => unreachable,
409 .elems => |elems| elems[index],
410 .repeated_elem => |elem| elem,
411 }) {
412 .bool_true => true,
413 .bool_false => false,
414 else => |elem| {
415 assert(mod.intern_pool.indexToKey(elem).undef == .bool_type);
416 continue;
417 },
418 }) byte.* |= mask else byte.* &= ~mask;
408419 }
409420 },
421 else => switch (aggregate.storage) {
422 .bytes => |bytes| try code.appendSlice(bytes),
423 .elems, .repeated_elem => {
424 var index: u64 = 0;
425 while (index < vector_type.len) : (index += 1) {
426 switch (try generateSymbol(bin_file, src_loc, .{
427 .ty = Type.fromInterned(vector_type.child),
428 .val = Value.fromInterned(switch (aggregate.storage) {
429 .bytes => unreachable,
430 .elems => |elems| elems[
431 math.cast(usize, index) orelse return error.Overflow
432 ],
433 .repeated_elem => |elem| elem,
434 }),
435 }, code, debug_output, reloc_info)) {
436 .ok => {},
437 .fail => |em| return .{ .fail = em },
438 }
439 }
440 },
441 },
410442 }
411443
412 const padding = math.cast(usize, typed_value.ty.abiSize(mod) -
413 (math.divCeil(u64, Type.fromInterned(vector_type.child).bitSize(mod) * vector_type.len, 8) catch |err| switch (err) {
444 const padding = abi_size - (math.cast(usize, math.divCeil(
445 u64,
446 Type.fromInterned(vector_type.child).bitSize(mod) * vector_type.len,
447 8,
448 ) catch |err| switch (err) {
414449 error.DivisionByZero => unreachable,
415450 else => |e| return e,
416 })) orelse return error.Overflow;
451 }) orelse return error.Overflow);
417452 if (padding > 0) try code.appendNTimes(0, padding);
418453 },
419454 .anon_struct_type => |tuple| {
test/behavior/bitcast.zig-1
......@@ -393,7 +393,6 @@ test "bitcast vector to integer and back" {
393393 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
394394 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
395395 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
396 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
397396 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
398397 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
399398
test/behavior/vector.zig-1
......@@ -1234,7 +1234,6 @@ test "array of vectors is copied" {
12341234
12351235test "byte vector initialized in inline function" {
12361236 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1237 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
12381237 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12391238 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12401239 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO