authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-04-28 20:39:38-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-05-01 19:22:52-04:00
log4ec49da5f6a6a8e77cdb66b8f814718bf11fffef
tree38e53fbeadbffa706bff35209613a090bcbd5201
parent7c9891d7b7d09060630693231702f27669dd0dc9

x86_64: implement a bunch of floating point stuff


7 files changed, 486 insertions(+), 116 deletions(-)

src/arch/x86_64/CodeGen.zig+358-80
......@@ -1297,9 +1297,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
12971297 .ceil,
12981298 .round,
12991299 .trunc_float,
1300 .neg,
13011300 => try self.airUnaryMath(inst),
13021301
1302 .neg => try self.airNeg(inst),
1303
13031304 .add_with_overflow => try self.airAddSubWithOverflow(inst),
13041305 .sub_with_overflow => try self.airAddSubWithOverflow(inst),
13051306 .mul_with_overflow => try self.airMulWithOverflow(inst),
......@@ -1881,7 +1882,7 @@ pub fn spillRegisters(self: *Self, registers: []const Register) !void {
18811882/// allocated. A second call to `copyToTmpRegister` may return the same register.
18821883/// This can have a side effect of spilling instructions to the stack to free up a register.
18831884fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
1884 const reg: Register = try self.register_manager.allocReg(null, try self.regClassForType(ty));
1885 const reg = try self.register_manager.allocReg(null, try self.regClassForType(ty));
18851886 try self.genSetReg(reg, ty, mcv);
18861887 return reg;
18871888}
......@@ -1924,16 +1925,48 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
19241925
19251926fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
19261927 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1927 _ = ty_op;
1928 return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
1929 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1928 const dst_ty = self.air.typeOfIndex(inst);
1929 const src_ty = self.air.typeOf(ty_op.operand);
1930 if (dst_ty.floatBits(self.target.*) != 32 or src_ty.floatBits(self.target.*) != 64 or
1931 !Target.x86.featureSetHas(self.target.cpu.features, .sse2))
1932 return self.fail("TODO implement airFptrunc from {} to {}", .{
1933 src_ty.fmt(self.bin_file.options.module.?),
1934 dst_ty.fmt(self.bin_file.options.module.?),
1935 });
1936
1937 const src_mcv = try self.resolveInst(ty_op.operand);
1938 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
1939 src_mcv
1940 else
1941 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv);
1942 const dst_lock = self.register_manager.lockReg(dst_mcv.register);
1943 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
1944
1945 try self.genBinOpMir(.cvtsd2ss, src_ty, dst_mcv, src_mcv);
1946 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
19301947}
19311948
19321949fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
19331950 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1934 _ = ty_op;
1935 return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
1936 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1951 const dst_ty = self.air.typeOfIndex(inst);
1952 const src_ty = self.air.typeOf(ty_op.operand);
1953 if (dst_ty.floatBits(self.target.*) != 64 or src_ty.floatBits(self.target.*) != 32 or
1954 !Target.x86.featureSetHas(self.target.cpu.features, .sse2))
1955 return self.fail("TODO implement airFpext from {} to {}", .{
1956 src_ty.fmt(self.bin_file.options.module.?),
1957 dst_ty.fmt(self.bin_file.options.module.?),
1958 });
1959
1960 const src_mcv = try self.resolveInst(ty_op.operand);
1961 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv))
1962 src_mcv
1963 else
1964 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv);
1965 const dst_lock = self.register_manager.lockReg(dst_mcv.register);
1966 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
1967
1968 try self.genBinOpMir(.cvtss2sd, src_ty, dst_mcv, src_mcv);
1969 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
19371970}
19381971
19391972fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
......@@ -3953,10 +3986,65 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
39533986 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
39543987}
39553988
3989fn airNeg(self: *Self, inst: Air.Inst.Index) !void {
3990 const un_op = self.air.instructions.items(.data)[inst].un_op;
3991 const ty = self.air.typeOf(un_op);
3992 const ty_bits = ty.floatBits(self.target.*);
3993
3994 var arena = std.heap.ArenaAllocator.init(self.gpa);
3995 defer arena.deinit();
3996
3997 const ExpectedContents = union {
3998 f16: Value.Payload.Float_16,
3999 f32: Value.Payload.Float_32,
4000 f64: Value.Payload.Float_64,
4001 f80: Value.Payload.Float_80,
4002 f128: Value.Payload.Float_128,
4003 };
4004 var stack align(@alignOf(ExpectedContents)) =
4005 std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator());
4006
4007 var vec_pl = Type.Payload.Array{
4008 .base = .{ .tag = .vector },
4009 .data = .{
4010 .len = @divExact(128, ty_bits),
4011 .elem_type = ty,
4012 },
4013 };
4014 const vec_ty = Type.initPayload(&vec_pl.base);
4015
4016 var sign_pl = Value.Payload.SubValue{
4017 .base = .{ .tag = .repeated },
4018 .data = try Value.floatToValue(-0.0, stack.get(), ty, self.target.*),
4019 };
4020 const sign_val = Value.initPayload(&sign_pl.base);
4021
4022 const sign_mcv = try self.genTypedValue(.{ .ty = vec_ty, .val = sign_val });
4023
4024 const src_mcv = try self.resolveInst(un_op);
4025 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv))
4026 src_mcv
4027 else
4028 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv);
4029 const dst_lock = self.register_manager.lockReg(dst_mcv.register);
4030 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
4031
4032 try self.genBinOpMir(switch (ty_bits) {
4033 32 => .xorps,
4034 64 => .xorpd,
4035 else => return self.fail("TODO implement airNeg for {}", .{
4036 ty.fmt(self.bin_file.options.module.?),
4037 }),
4038 }, vec_ty, dst_mcv, sign_mcv);
4039 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
4040}
4041
39564042fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
39574043 const un_op = self.air.instructions.items(.data)[inst].un_op;
39584044 _ = un_op;
3959 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
4045 return self.fail("TODO implement airUnaryMath for {}", .{
4046 self.air.instructions.items(.tag)[inst],
4047 });
39604048 //return self.finishAir(inst, result, .{ un_op, .none, .none });
39614049}
39624050
......@@ -4109,7 +4197,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro
41094197fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
41104198 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
41114199 const elem_ty = self.air.typeOfIndex(inst);
4112 const elem_size = elem_ty.abiSize(self.target.*);
41134200 const result: MCValue = result: {
41144201 if (!elem_ty.hasRuntimeBitsIgnoreComptime()) break :result .none;
41154202
......@@ -4117,14 +4204,20 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
41174204 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx });
41184205 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);
41194206
4207 const ptr_ty = self.air.typeOf(ty_op.operand);
4208 const elem_size = elem_ty.abiSize(self.target.*);
4209
4210 const elem_rc = try self.regClassForType(elem_ty);
4211 const ptr_rc = try self.regClassForType(ptr_ty);
4212
41204213 const ptr_mcv = try self.resolveInst(ty_op.operand);
4121 const dst_mcv = if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr_mcv))
4214 const dst_mcv = if (elem_size <= 8 and elem_rc.supersetOf(ptr_rc) and
4215 self.reuseOperand(inst, ty_op.operand, 0, ptr_mcv))
41224216 // The MCValue that holds the pointer can be re-used as the value.
41234217 ptr_mcv
41244218 else
41254219 try self.allocRegOrMem(inst, true);
41264220
4127 const ptr_ty = self.air.typeOf(ty_op.operand);
41284221 if (ptr_ty.ptrInfo().data.host_size > 0) {
41294222 try self.packedLoad(dst_mcv, ptr_ty, ptr_mcv);
41304223 } else {
......@@ -4346,17 +4439,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
43464439
43474440 switch (src_mcv) {
43484441 .load_frame => |frame_addr| {
4349 const field_abi_size = @intCast(u32, field_ty.abiSize(self.target.*));
4350 const limb_abi_size = @min(field_abi_size, 8);
4351 const limb_abi_bits = limb_abi_size * 8;
4352 const field_byte_off = @intCast(i32, field_off / limb_abi_bits * limb_abi_size);
4353 const field_bit_off = field_off % limb_abi_bits;
4354
4355 if (field_bit_off == 0) {
4356 const off_mcv = MCValue{ .load_frame = .{
4357 .index = frame_addr.index,
4358 .off = frame_addr.off + field_byte_off,
4359 } };
4442 if (field_off % 8 == 0) {
4443 const off_mcv =
4444 src_mcv.address().offset(@intCast(i32, @divExact(field_off, 8))).deref();
43604445 if (self.reuseOperand(inst, operand, 0, src_mcv)) break :result off_mcv;
43614446
43624447 const dst_mcv = try self.allocRegOrMem(inst, true);
......@@ -4364,6 +4449,12 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
43644449 break :result dst_mcv;
43654450 }
43664451
4452 const field_abi_size = @intCast(u32, field_ty.abiSize(self.target.*));
4453 const limb_abi_size = @min(field_abi_size, 8);
4454 const limb_abi_bits = limb_abi_size * 8;
4455 const field_byte_off = @intCast(i32, field_off / limb_abi_bits * limb_abi_size);
4456 const field_bit_off = field_off % limb_abi_bits;
4457
43674458 if (field_abi_size > 8) {
43684459 return self.fail("TODO implement struct_field_val with large packed field", .{});
43694460 }
......@@ -5181,24 +5272,69 @@ fn genBinOp(
51815272 switch (tag) {
51825273 .add,
51835274 .addwrap,
5184 => try self.genBinOpMir(switch (lhs_ty.tag()) {
5275 => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) {
51855276 else => .add,
5186 .f32 => .addss,
5187 .f64 => .addsd,
5277 .Float => switch (lhs_ty.floatBits(self.target.*)) {
5278 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse))
5279 .addss
5280 else
5281 return self.fail("TODO implement genBinOp for {s} {} without sse", .{
5282 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5283 }),
5284 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2))
5285 .addsd
5286 else
5287 return self.fail("TODO implement genBinOp for {s} {} without sse2", .{
5288 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5289 }),
5290 else => return self.fail("TODO implement genBinOp for {s} {}", .{
5291 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5292 }),
5293 },
51885294 }, lhs_ty, dst_mcv, src_mcv),
51895295
51905296 .sub,
51915297 .subwrap,
5192 => try self.genBinOpMir(switch (lhs_ty.tag()) {
5298 => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) {
51935299 else => .sub,
5194 .f32 => .subss,
5195 .f64 => .subsd,
5300 .Float => switch (lhs_ty.floatBits(self.target.*)) {
5301 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse))
5302 .subss
5303 else
5304 return self.fail("TODO implement genBinOp for {s} {} without sse", .{
5305 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5306 }),
5307 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2))
5308 .subsd
5309 else
5310 return self.fail("TODO implement genBinOp for {s} {} without sse2", .{
5311 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5312 }),
5313 else => return self.fail("TODO implement genBinOp for {s} {}", .{
5314 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5315 }),
5316 },
51965317 }, lhs_ty, dst_mcv, src_mcv),
51975318
5198 .mul => try self.genBinOpMir(switch (lhs_ty.tag()) {
5199 .f32 => .mulss,
5200 .f64 => .mulsd,
5319 .mul => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) {
52015320 else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }),
5321 .Float => switch (lhs_ty.floatBits(self.target.*)) {
5322 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse))
5323 .mulss
5324 else
5325 return self.fail("TODO implement genBinOp for {s} {} without sse", .{
5326 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5327 }),
5328 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2))
5329 .mulsd
5330 else
5331 return self.fail("TODO implement genBinOp for {s} {} without sse2", .{
5332 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5333 }),
5334 else => return self.fail("TODO implement genBinOp for {s} {}", .{
5335 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5336 }),
5337 },
52025338 }, lhs_ty, dst_mcv, src_mcv),
52035339
52045340 .div_float,
......@@ -5206,12 +5342,27 @@ fn genBinOp(
52065342 .div_trunc,
52075343 .div_floor,
52085344 => {
5209 try self.genBinOpMir(switch (lhs_ty.tag()) {
5210 .f32 => .divss,
5211 .f64 => .divsd,
5345 try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) {
52125346 else => return self.fail("TODO implement genBinOp for {s} {}", .{
52135347 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
52145348 }),
5349 .Float => switch (lhs_ty.floatBits(self.target.*)) {
5350 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse))
5351 .divss
5352 else
5353 return self.fail("TODO implement genBinOp for {s} {} without sse", .{
5354 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5355 }),
5356 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2))
5357 .divsd
5358 else
5359 return self.fail("TODO implement genBinOp for {s} {} without sse2", .{
5360 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5361 }),
5362 else => return self.fail("TODO implement genBinOp for {s} {}", .{
5363 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5364 }),
5365 },
52155366 }, lhs_ty, dst_mcv, src_mcv);
52165367 switch (tag) {
52175368 .div_float,
......@@ -5222,16 +5373,18 @@ fn genBinOp(
52225373 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse4_1)) {
52235374 const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*));
52245375 const dst_alias = registerAlias(dst_mcv.register, abi_size);
5225 try self.asmRegisterRegisterImmediate(switch (lhs_ty.tag()) {
5226 .f32 => .roundss,
5227 .f64 => .roundsd,
5376 try self.asmRegisterRegisterImmediate(switch (lhs_ty.floatBits(self.target.*)) {
5377 32 => .roundss,
5378 64 => .roundsd,
52285379 else => unreachable,
52295380 }, dst_alias, dst_alias, Immediate.u(switch (tag) {
52305381 .div_trunc => 0b1_0_11,
52315382 .div_floor => 0b1_0_01,
52325383 else => unreachable,
52335384 }));
5234 } else return self.fail("TODO implement round without sse4_1", .{}),
5385 } else return self.fail("TODO implement genBinOp for {s} {} without sse4_1", .{
5386 @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?),
5387 }),
52355388 else => unreachable,
52365389 }
52375390 },
......@@ -5453,39 +5606,68 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s
54535606 )),
54545607 else => unreachable,
54555608 },
5456 .register_offset,
54575609 .eflags,
5610 .register_offset,
54585611 .memory,
5612 .indirect,
54595613 .load_direct,
54605614 .lea_direct,
54615615 .load_got,
54625616 .lea_got,
54635617 .load_tlv,
54645618 .lea_tlv,
5619 .load_frame,
54655620 .lea_frame,
54665621 => {
5467 assert(abi_size <= 8);
5622 blk: {
5623 return self.asmRegisterMemory(
5624 mir_tag,
5625 registerAlias(dst_reg, abi_size),
5626 Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (src_mcv) {
5627 .memory => |addr| .{
5628 .base = .{ .reg = .ds },
5629 .disp = math.cast(i32, addr) orelse break :blk,
5630 },
5631 .indirect => |reg_off| .{
5632 .base = .{ .reg = reg_off.reg },
5633 .disp = reg_off.off,
5634 },
5635 .load_frame => |frame_addr| .{
5636 .base = .{ .frame = frame_addr.index },
5637 .disp = frame_addr.off,
5638 },
5639 else => break :blk,
5640 }),
5641 );
5642 }
5643
54685644 const dst_reg_lock = self.register_manager.lockReg(dst_reg);
54695645 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
54705646
5471 const reg = try self.copyToTmpRegister(ty, src_mcv);
5472 return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg });
5473 },
5474 .indirect, .load_frame => try self.asmRegisterMemory(
5475 mir_tag,
5476 registerAlias(dst_reg, abi_size),
5477 Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (src_mcv) {
5478 .indirect => |reg_off| .{
5479 .base = .{ .reg = reg_off.reg },
5480 .disp = reg_off.off,
5647 switch (src_mcv) {
5648 .eflags,
5649 .register_offset,
5650 .lea_direct,
5651 .lea_got,
5652 .lea_tlv,
5653 .lea_frame,
5654 => {
5655 const reg = try self.copyToTmpRegister(ty, src_mcv);
5656 return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg });
54815657 },
5482 .load_frame => |frame_addr| .{
5483 .base = .{ .frame = frame_addr.index },
5484 .disp = frame_addr.off,
5658 .memory,
5659 .load_direct,
5660 .load_got,
5661 .load_tlv,
5662 => {
5663 const addr_reg = try self.copyToTmpRegister(ty, src_mcv.address());
5664 return self.genBinOpMir(mir_tag, ty, dst_mcv, .{
5665 .indirect = .{ .reg = addr_reg },
5666 });
54855667 },
54865668 else => unreachable,
5487 }),
5488 ),
5669 }
5670 },
54895671 }
54905672 },
54915673 .memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => {
......@@ -6175,10 +6357,25 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
61756357 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock);
61766358
61776359 const src_mcv = if (flipped) lhs_mcv else rhs_mcv;
6178 try self.genBinOpMir(switch (ty.tag()) {
6360 try self.genBinOpMir(switch (ty.zigTypeTag()) {
61796361 else => .cmp,
6180 .f32 => .ucomiss,
6181 .f64 => .ucomisd,
6362 .Float => switch (ty.floatBits(self.target.*)) {
6363 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse))
6364 .ucomiss
6365 else
6366 return self.fail("TODO implement airCmp for {} without sse", .{
6367 ty.fmt(self.bin_file.options.module.?),
6368 }),
6369 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2))
6370 .ucomisd
6371 else
6372 return self.fail("TODO implement airCmp for {} without sse2", .{
6373 ty.fmt(self.bin_file.options.module.?),
6374 }),
6375 else => return self.fail("TODO implement airCmp for {}", .{
6376 ty.fmt(self.bin_file.options.module.?),
6377 }),
6378 },
61826379 }, ty, dst_mcv, src_mcv);
61836380
61846381 const signedness = if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned;
......@@ -7608,7 +7805,8 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
76087805 const dst_rc = try self.regClassForType(dst_ty);
76097806 const src_rc = try self.regClassForType(src_ty);
76107807 const operand = try self.resolveInst(ty_op.operand);
7611 if (dst_rc.eql(src_rc) and self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand;
7808 if (dst_rc.supersetOf(src_rc) and self.reuseOperand(inst, ty_op.operand, 0, operand))
7809 break :result operand;
76127810
76137811 const operand_lock = switch (operand) {
76147812 .register => |reg| self.register_manager.lockReg(reg),
......@@ -7648,9 +7846,59 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
76487846
76497847fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
76507848 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
7651 _ = ty_op;
7652 return self.fail("TODO implement airIntToFloat for {}", .{self.target.cpu.arch});
7653 //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
7849
7850 const src_ty = self.air.typeOf(ty_op.operand);
7851 const src_bits = @intCast(u32, src_ty.bitSize(self.target.*));
7852 const src_signedness =
7853 if (src_ty.isAbiInt()) src_ty.intInfo(self.target.*).signedness else .unsigned;
7854 const dst_ty = self.air.typeOfIndex(inst);
7855
7856 const src_size = std.math.divCeil(u32, @max(switch (src_signedness) {
7857 .signed => src_bits,
7858 .unsigned => src_bits + 1,
7859 }, 32), 8) catch unreachable;
7860 if (src_size > 8) return self.fail("TODO implement airIntToFloat from {} to {}", .{
7861 src_ty.fmt(self.bin_file.options.module.?),
7862 dst_ty.fmt(self.bin_file.options.module.?),
7863 });
7864
7865 const src_mcv = try self.resolveInst(ty_op.operand);
7866 const src_reg = switch (src_mcv) {
7867 .register => |reg| reg,
7868 else => try self.copyToTmpRegister(src_ty, src_mcv),
7869 };
7870 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
7871 defer self.register_manager.unlockReg(src_lock);
7872
7873 if (src_bits < src_size * 8) try self.truncateRegister(src_ty, src_reg);
7874
7875 const dst_reg = try self.register_manager.allocReg(inst, try self.regClassForType(dst_ty));
7876 const dst_mcv = MCValue{ .register = dst_reg };
7877 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
7878 defer self.register_manager.unlockReg(dst_lock);
7879
7880 try self.asmRegisterRegister(switch (dst_ty.floatBits(self.target.*)) {
7881 32 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse))
7882 .cvtsi2ss
7883 else
7884 return self.fail("TODO implement airIntToFloat from {} to {} without sse", .{
7885 src_ty.fmt(self.bin_file.options.module.?),
7886 dst_ty.fmt(self.bin_file.options.module.?),
7887 }),
7888 64 => if (Target.x86.featureSetHas(self.target.cpu.features, .sse2))
7889 .cvtsi2sd
7890 else
7891 return self.fail("TODO implement airIntToFloat from {} to {} without sse2", .{
7892 src_ty.fmt(self.bin_file.options.module.?),
7893 dst_ty.fmt(self.bin_file.options.module.?),
7894 }),
7895 else => return self.fail("TODO implement airIntToFloat from {} to {}", .{
7896 src_ty.fmt(self.bin_file.options.module.?),
7897 dst_ty.fmt(self.bin_file.options.module.?),
7898 }),
7899 }, dst_reg.to128(), registerAlias(src_reg, src_size));
7900
7901 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
76547902}
76557903
76567904fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
......@@ -8717,6 +8965,7 @@ fn resolveCallingConventionValues(
87178965 },
87188966 .C => {
87198967 var param_reg_i: usize = 0;
8968 var param_sse_reg_i: usize = 0;
87208969 result.stack_align = 16;
87218970
87228971 switch (self.target.os.tag) {
......@@ -8734,26 +8983,39 @@ fn resolveCallingConventionValues(
87348983 // TODO: is this even possible for C calling convention?
87358984 result.return_value = InstTracking.init(.none);
87368985 } else {
8737 const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0];
8738 const ret_ty_size = @intCast(u31, ret_ty.abiSize(self.target.*));
8739 if (ret_ty_size <= 8) {
8740 const aliased_reg = registerAlias(ret_reg, ret_ty_size);
8741 result.return_value = .{ .short = .{ .register = aliased_reg }, .long = .none };
8742 } else {
8743 const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i];
8744 param_reg_i += 1;
8745 result.return_value = .{
8746 .short = .{ .indirect = .{ .reg = ret_reg } },
8747 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
8748 };
8986 const classes = switch (self.target.os.tag) {
8987 .windows => &[1]abi.Class{abi.classifyWindows(ret_ty, self.target.*)},
8988 else => mem.sliceTo(&abi.classifySystemV(ret_ty, self.target.*, .ret), .none),
8989 };
8990 if (classes.len > 1) {
8991 return self.fail("TODO handle multiple classes per type", .{});
87498992 }
8993 const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0];
8994 result.return_value = switch (classes[0]) {
8995 .integer => InstTracking.init(.{ .register = registerAlias(
8996 ret_reg,
8997 @intCast(u32, ret_ty.abiSize(self.target.*)),
8998 ) }),
8999 .float, .sse => InstTracking.init(.{ .register = .xmm0 }),
9000 .memory => ret: {
9001 const ret_indirect_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i];
9002 param_reg_i += 1;
9003 break :ret .{
9004 .short = .{ .indirect = .{ .reg = ret_reg } },
9005 .long = .{ .indirect = .{ .reg = ret_indirect_reg } },
9006 };
9007 },
9008 else => |class| return self.fail("TODO handle calling convention class {s}", .{
9009 @tagName(class),
9010 }),
9011 };
87509012 }
87519013
87529014 // Input params
87539015 for (param_types, result.args) |ty, *arg| {
87549016 assert(ty.hasRuntimeBitsIgnoreComptime());
87559017
8756 const classes: []const abi.Class = switch (self.target.os.tag) {
9018 const classes = switch (self.target.os.tag) {
87579019 .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)},
87589020 else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*, .arg), .none),
87599021 };
......@@ -8761,13 +9023,29 @@ fn resolveCallingConventionValues(
87619023 return self.fail("TODO handle multiple classes per type", .{});
87629024 }
87639025 switch (classes[0]) {
8764 .integer => blk: {
8765 if (param_reg_i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk;
8766 const param_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i];
9026 .integer => if (param_reg_i < abi.getCAbiIntParamRegs(self.target.*).len) {
9027 arg.* = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i] };
87679028 param_reg_i += 1;
8768 arg.* = .{ .register = param_reg };
87699029 continue;
87709030 },
9031 .float, .sse => switch (self.target.os.tag) {
9032 .windows => if (param_reg_i < 4) {
9033 arg.* = .{ .register = @intToEnum(
9034 Register,
9035 @enumToInt(Register.xmm0) + param_reg_i,
9036 ) };
9037 param_reg_i += 1;
9038 continue;
9039 },
9040 else => if (param_sse_reg_i < 8) {
9041 arg.* = .{ .register = @intToEnum(
9042 Register,
9043 @enumToInt(Register.xmm0) + param_sse_reg_i,
9044 ) };
9045 param_sse_reg_i += 1;
9046 continue;
9047 },
9048 },
87719049 .memory => {}, // fallthrough
87729050 else => |class| return self.fail("TODO handle calling convention class {s}", .{
87739051 @tagName(class),
src/arch/x86_64/Encoding.zig+16-11
......@@ -58,7 +58,7 @@ pub fn findByMnemonic(
5858 next: for (mnemonic_to_encodings_map[@enumToInt(mnemonic)]) |data| {
5959 switch (data.mode) {
6060 .rex => if (!rex_required) continue,
61 .long, .sse2_long => {},
61 .long, .sse_long, .sse2_long => {},
6262 else => if (rex_required) continue,
6363 }
6464 for (input_ops, data.ops) |input_op, data_op|
......@@ -90,7 +90,7 @@ pub fn findByOpcode(opc: []const u8, prefixes: struct {
9090 if (prefixes.rex.w) {
9191 switch (data.mode) {
9292 .short, .fpu, .sse, .sse2, .sse4_1, .none => continue,
93 .long, .sse2_long, .rex => {},
93 .long, .sse_long, .sse2_long, .rex => {},
9494 }
9595 } else if (prefixes.rex.present and !prefixes.rex.isSet()) {
9696 switch (data.mode) {
......@@ -138,7 +138,7 @@ pub fn modRmExt(encoding: Encoding) u3 {
138138pub fn operandBitSize(encoding: Encoding) u64 {
139139 switch (encoding.data.mode) {
140140 .short => return 16,
141 .long, .sse2_long => return 64,
141 .long, .sse_long, .sse2_long => return 64,
142142 else => {},
143143 }
144144 const bit_size: u64 = switch (encoding.data.op_en) {
......@@ -163,7 +163,7 @@ pub fn format(
163163 _ = options;
164164 _ = fmt;
165165 switch (encoding.data.mode) {
166 .long, .sse2_long => try writer.writeAll("REX.W + "),
166 .long, .sse_long, .sse2_long => try writer.writeAll("REX.W + "),
167167 else => {},
168168 }
169169
......@@ -269,21 +269,25 @@ pub const Mnemonic = enum {
269269 // SSE
270270 addss,
271271 cmpss,
272 cvtsi2ss,
272273 divss,
273274 maxss, minss,
274275 movss,
275276 mulss,
276277 subss,
277278 ucomiss,
279 xorps,
278280 // SSE2
279281 addsd,
280282 //cmpsd,
283 cvtsd2ss, cvtsi2sd, cvtss2sd,
281284 divsd,
282285 maxsd, minsd,
283286 movq, //movd, movsd,
284287 mulsd,
285288 subsd,
286289 ucomisd,
290 xorpd,
287291 // SSE4.1
288292 roundss,
289293 roundsd,
......@@ -318,7 +322,7 @@ pub const Op = enum {
318322 m,
319323 moffs,
320324 sreg,
321 xmm, xmm_m32, xmm_m64,
325 xmm, xmm_m32, xmm_m64, xmm_m128,
322326 // zig fmt: on
323327
324328 pub fn fromOperand(operand: Instruction.Operand) Op {
......@@ -400,7 +404,7 @@ pub const Op = enum {
400404 .imm32, .imm32s, .eax, .r32, .m32, .rm32, .rel32, .xmm_m32 => 32,
401405 .imm64, .rax, .r64, .m64, .rm64, .xmm_m64 => 64,
402406 .m80 => 80,
403 .m128, .xmm => 128,
407 .m128, .xmm, .xmm_m128 => 128,
404408 };
405409 }
406410
......@@ -423,8 +427,8 @@ pub const Op = enum {
423427 .al, .ax, .eax, .rax,
424428 .r8, .r16, .r32, .r64,
425429 .rm8, .rm16, .rm32, .rm64,
426 .xmm, .xmm_m32, .xmm_m64,
427 => true,
430 .xmm, .xmm_m32, .xmm_m64, .xmm_m128,
431 => true,
428432 else => false,
429433 };
430434 // zig fmt: on
......@@ -449,7 +453,7 @@ pub const Op = enum {
449453 .rm8, .rm16, .rm32, .rm64,
450454 .m8, .m16, .m32, .m64, .m80, .m128,
451455 .m,
452 .xmm_m32, .xmm_m64,
456 .xmm_m32, .xmm_m64, .xmm_m128,
453457 => true,
454458 else => false,
455459 };
......@@ -470,13 +474,13 @@ pub const Op = enum {
470474 .r8, .r16, .r32, .r64 => .general_purpose,
471475 .rm8, .rm16, .rm32, .rm64 => .general_purpose,
472476 .sreg => .segment,
473 .xmm, .xmm_m32, .xmm_m64 => .floating_point,
477 .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => .floating_point,
474478 };
475479 }
476480
477481 pub fn isFloatingPointRegister(op: Op) bool {
478482 return switch (op) {
479 .xmm, .xmm_m32, .xmm_m64 => true,
483 .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => true,
480484 else => false,
481485 };
482486 }
......@@ -535,6 +539,7 @@ pub const Mode = enum {
535539 rex,
536540 long,
537541 sse,
542 sse_long,
538543 sse2,
539544 sse2_long,
540545 sse4_1,
src/arch/x86_64/Lower.zig+6
......@@ -95,6 +95,7 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
9595
9696 .addss,
9797 .cmpss,
98 .cvtsi2ss,
9899 .divss,
99100 .maxss,
100101 .minss,
......@@ -103,8 +104,12 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
103104 .roundss,
104105 .subss,
105106 .ucomiss,
107 .xorps,
106108 .addsd,
107109 .cmpsd,
110 .cvtsd2ss,
111 .cvtsi2sd,
112 .cvtss2sd,
108113 .divsd,
109114 .maxsd,
110115 .minsd,
......@@ -113,6 +118,7 @@ pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction {
113118 .roundsd,
114119 .subsd,
115120 .ucomisd,
121 .xorpd,
116122 => try lower.mirGeneric(inst),
117123
118124 .cmps,
src/arch/x86_64/Mir.zig+12
......@@ -170,6 +170,8 @@ pub const Inst = struct {
170170 addss,
171171 /// Compare scalar single-precision floating-point values
172172 cmpss,
173 /// Convert doubleword integer to scalar single-precision floating-point value
174 cvtsi2ss,
173175 /// Divide scalar single-precision floating-point values
174176 divss,
175177 /// Return maximum single-precision floating-point value
......@@ -186,10 +188,18 @@ pub const Inst = struct {
186188 subss,
187189 /// Unordered compare scalar single-precision floating-point values
188190 ucomiss,
191 /// Bitwise logical xor of packed single precision floating-point values
192 xorps,
189193 /// Add double precision floating point values
190194 addsd,
191195 /// Compare scalar double-precision floating-point values
192196 cmpsd,
197 /// Convert scalar double-precision floating-point value to scalar single-precision floating-point value
198 cvtsd2ss,
199 /// Convert doubleword integer to scalar double-precision floating-point value
200 cvtsi2sd,
201 /// Convert scalar single-precision floating-point value to scalar double-precision floating-point value
202 cvtss2sd,
193203 /// Divide scalar double-precision floating-point values
194204 divsd,
195205 /// Return maximum double-precision floating-point value
......@@ -206,6 +216,8 @@ pub const Inst = struct {
206216 subsd,
207217 /// Unordered compare scalar double-precision floating-point values
208218 ucomisd,
219 /// Bitwise logical xor of packed double precision floating-point values
220 xorpd,
209221
210222 /// Compare string operands
211223 cmps,
src/arch/x86_64/encoder.zig+1-1
......@@ -323,7 +323,7 @@ pub const Instruction = struct {
323323 var rex = Rex{};
324324 rex.present = inst.encoding.data.mode == .rex;
325325 switch (inst.encoding.data.mode) {
326 .long, .sse2_long => rex.w = true,
326 .long, .sse_long, .sse2_long => rex.w = true,
327327 else => {},
328328 }
329329
src/arch/x86_64/encodings.zig+14
......@@ -834,6 +834,9 @@ pub const table = [_]Entry{
834834
835835 .{ .cmpss, .rmi, &.{ .xmm, .xmm_m32, .imm8 }, &.{ 0xf3, 0x0f, 0xc2 }, 0, .sse },
836836
837 .{ .cvtsi2ss, .rm, &.{ .xmm, .rm32 }, &.{ 0xf3, 0x0f, 0x2a }, 0, .sse },
838 .{ .cvtsi2ss, .rm, &.{ .xmm, .rm64 }, &.{ 0xf3, 0x0f, 0x2a }, 0, .sse_long },
839
837840 .{ .divss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5e }, 0, .sse },
838841
839842 .{ .maxss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5f }, 0, .sse },
......@@ -849,11 +852,20 @@ pub const table = [_]Entry{
849852
850853 .{ .ucomiss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x0f, 0x2e }, 0, .sse },
851854
855 .{ .xorps, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x0f, 0x57 }, 0, .sse },
856
852857 // SSE2
853858 .{ .addsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x58 }, 0, .sse2 },
854859
855860 .{ .cmpsd, .rmi, &.{ .xmm, .xmm_m64, .imm8 }, &.{ 0xf2, 0x0f, 0xc2 }, 0, .sse2 },
856861
862 .{ .cvtsd2ss, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5a }, 0, .sse2 },
863
864 .{ .cvtsi2sd, .rm, &.{ .xmm, .rm32 }, &.{ 0xf2, 0x0f, 0x2a }, 0, .sse2 },
865 .{ .cvtsi2sd, .rm, &.{ .xmm, .rm64 }, &.{ 0xf2, 0x0f, 0x2a }, 0, .sse2_long },
866
867 .{ .cvtss2sd, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5a }, 0, .sse2 },
868
857869 .{ .divsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5e }, 0, .sse2 },
858870
859871 .{ .maxsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5f }, 0, .sse2 },
......@@ -878,6 +890,8 @@ pub const table = [_]Entry{
878890
879891 .{ .ucomisd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x2e }, 0, .sse2 },
880892
893 .{ .xorpd, .rm, &.{ .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x57 }, 0, .sse2 },
894
881895 // SSE4.1
882896 .{ .roundss, .rmi, &.{ .xmm, .xmm_m32, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0a }, 0, .sse4_1 },
883897 .{ .roundsd, .rmi, &.{ .xmm, .xmm_m64, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0b }, 0, .sse4_1 },
src/codegen.zig+79-24
......@@ -291,6 +291,20 @@ pub fn generateSymbol(
291291 },
292292 },
293293 .Pointer => switch (typed_value.val.tag()) {
294 .null_value => {
295 switch (target.cpu.arch.ptrBitWidth()) {
296 32 => {
297 mem.writeInt(u32, try code.addManyAsArray(4), 0, endian);
298 if (typed_value.ty.isSlice()) try code.appendNTimes(0xaa, 4);
299 },
300 64 => {
301 mem.writeInt(u64, try code.addManyAsArray(8), 0, endian);
302 if (typed_value.ty.isSlice()) try code.appendNTimes(0xaa, 8);
303 },
304 else => unreachable,
305 }
306 return Result.ok;
307 },
294308 .zero, .one, .int_u64, .int_big_positive => {
295309 switch (target.cpu.arch.ptrBitWidth()) {
296310 32 => {
......@@ -397,30 +411,15 @@ pub fn generateSymbol(
397411 },
398412 }
399413 },
400 .elem_ptr => {
401 const elem_ptr = typed_value.val.castTag(.elem_ptr).?.data;
402 const elem_size = typed_value.ty.childType().abiSize(target);
403 const addend = @intCast(u32, elem_ptr.index * elem_size);
404 const array_ptr = elem_ptr.array_ptr;
405
406 switch (array_ptr.tag()) {
407 .decl_ref => {
408 const decl_index = array_ptr.castTag(.decl_ref).?.data;
409 return lowerDeclRef(bin_file, src_loc, typed_value, decl_index, code, debug_output, .{
410 .parent_atom_index = reloc_info.parent_atom_index,
411 .addend = (reloc_info.addend orelse 0) + addend,
412 });
413 },
414 else => return Result{
415 .fail = try ErrorMsg.create(
416 bin_file.allocator,
417 src_loc,
418 "TODO implement generateSymbol for pointer type value: '{s}'",
419 .{@tagName(typed_value.val.tag())},
420 ),
421 },
422 }
423 },
414 .elem_ptr => return lowerParentPtr(
415 bin_file,
416 src_loc,
417 typed_value,
418 typed_value.val,
419 code,
420 debug_output,
421 reloc_info,
422 ),
424423 else => return Result{
425424 .fail = try ErrorMsg.create(
426425 bin_file.allocator,
......@@ -838,9 +837,62 @@ pub fn generateSymbol(
838837 }
839838}
840839
840fn lowerParentPtr(
841 bin_file: *link.File,
842 src_loc: Module.SrcLoc,
843 typed_value: TypedValue,
844 parent_ptr: Value,
845 code: *std.ArrayList(u8),
846 debug_output: DebugInfoOutput,
847 reloc_info: RelocInfo,
848) CodeGenError!Result {
849 const target = bin_file.options.target;
850
851 switch (parent_ptr.tag()) {
852 .elem_ptr => {
853 const elem_ptr = parent_ptr.castTag(.elem_ptr).?.data;
854 return lowerParentPtr(
855 bin_file,
856 src_loc,
857 typed_value,
858 elem_ptr.array_ptr,
859 code,
860 debug_output,
861 reloc_info.offset(@intCast(u32, elem_ptr.index * elem_ptr.elem_ty.abiSize(target))),
862 );
863 },
864 .decl_ref => {
865 const decl_index = parent_ptr.castTag(.decl_ref).?.data;
866 return lowerDeclRef(
867 bin_file,
868 src_loc,
869 typed_value,
870 decl_index,
871 code,
872 debug_output,
873 reloc_info,
874 );
875 },
876 else => |t| {
877 return Result{
878 .fail = try ErrorMsg.create(
879 bin_file.allocator,
880 src_loc,
881 "TODO implement lowerParentPtr for type '{s}'",
882 .{@tagName(t)},
883 ),
884 };
885 },
886 }
887}
888
841889const RelocInfo = struct {
842890 parent_atom_index: u32,
843891 addend: ?u32 = null,
892
893 fn offset(ri: RelocInfo, addend: u32) RelocInfo {
894 return .{ .parent_atom_index = ri.parent_atom_index, .addend = (ri.addend orelse 0) + addend };
895 }
844896};
845897
846898fn lowerDeclRef(
......@@ -1095,6 +1147,9 @@ pub fn genTypedValue(
10951147 .Slice => {},
10961148 else => {
10971149 switch (typed_value.val.tag()) {
1150 .null_value => {
1151 return GenResult.mcv(.{ .immediate = 0 });
1152 },
10981153 .int_u64 => {
10991154 return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(target) });
11001155 },