| ... | @@ -1297,9 +1297,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -1297,9 +1297,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 1297 | .ceil, | 1297 | .ceil, |
| 1298 | .round, | 1298 | .round, |
| 1299 | .trunc_float, | 1299 | .trunc_float, |
| 1300 | .neg, | | |
| 1301 | => try self.airUnaryMath(inst), | 1300 | => try self.airUnaryMath(inst), |
| 1302 | | 1301 | |
| | 1302 | .neg => try self.airNeg(inst), |
| | 1303 | |
| 1303 | .add_with_overflow => try self.airAddSubWithOverflow(inst), | 1304 | .add_with_overflow => try self.airAddSubWithOverflow(inst), |
| 1304 | .sub_with_overflow => try self.airAddSubWithOverflow(inst), | 1305 | .sub_with_overflow => try self.airAddSubWithOverflow(inst), |
| 1305 | .mul_with_overflow => try self.airMulWithOverflow(inst), | 1306 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| ... | @@ -1881,7 +1882,7 @@ pub fn spillRegisters(self: *Self, registers: []const Register) !void { | ... | @@ -1881,7 +1882,7 @@ pub fn spillRegisters(self: *Self, registers: []const Register) !void { |
| 1881 | /// allocated. A second call to `copyToTmpRegister` may return the same register. | 1882 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 1882 | /// This can have a side effect of spilling instructions to the stack to free up a register. | 1883 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 1883 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { | 1884 | fn 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)); |
| 1885 | try self.genSetReg(reg, ty, mcv); | 1886 | try self.genSetReg(reg, ty, mcv); |
| 1886 | return reg; | 1887 | return reg; |
| 1887 | } | 1888 | } |
| ... | @@ -1924,16 +1925,48 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1924,16 +1925,48 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1924 | | 1925 | |
| 1925 | fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { | 1926 | fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 1926 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1927 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1927 | _ = ty_op; | 1928 | const dst_ty = self.air.typeOfIndex(inst); |
| 1928 | return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}); | 1929 | const src_ty = self.air.typeOf(ty_op.operand); |
| 1929 | // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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 }); |
| 1930 | } | 1947 | } |
| 1931 | | 1948 | |
| 1932 | fn airFpext(self: *Self, inst: Air.Inst.Index) !void { | 1949 | fn airFpext(self: *Self, inst: Air.Inst.Index) !void { |
| 1933 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1950 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1934 | _ = ty_op; | 1951 | const dst_ty = self.air.typeOfIndex(inst); |
| 1935 | return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}); | 1952 | const src_ty = self.air.typeOf(ty_op.operand); |
| 1936 | // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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 }); |
| 1937 | } | 1970 | } |
| 1938 | | 1971 | |
| 1939 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { | 1972 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -3953,10 +3986,65 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3953,10 +3986,65 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { |
| 3953 | return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); | 3986 | return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); |
| 3954 | } | 3987 | } |
| 3955 | | 3988 | |
| | 3989 | fn 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 | |
| 3956 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { | 4042 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 3957 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 4043 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 3958 | _ = un_op; | 4044 | _ = 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 | }); |
| 3960 | //return self.finishAir(inst, result, .{ un_op, .none, .none }); | 4048 | //return self.finishAir(inst, result, .{ un_op, .none, .none }); |
| 3961 | } | 4049 | } |
| 3962 | | 4050 | |
| ... | @@ -4109,7 +4197,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro | ... | @@ -4109,7 +4197,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro |
| 4109 | fn airLoad(self: *Self, inst: Air.Inst.Index) !void { | 4197 | fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 4110 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 4198 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 4111 | const elem_ty = self.air.typeOfIndex(inst); | 4199 | const elem_ty = self.air.typeOfIndex(inst); |
| 4112 | const elem_size = elem_ty.abiSize(self.target.*); | | |
| 4113 | const result: MCValue = result: { | 4200 | const result: MCValue = result: { |
| 4114 | if (!elem_ty.hasRuntimeBitsIgnoreComptime()) break :result .none; | 4201 | if (!elem_ty.hasRuntimeBitsIgnoreComptime()) break :result .none; |
| 4115 | | 4202 | |
| ... | @@ -4117,14 +4204,20 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4117,14 +4204,20 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 4117 | const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); | 4204 | const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); |
| 4118 | defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); | 4205 | defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); |
| 4119 | | 4206 | |
| | 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 | |
| 4120 | const ptr_mcv = try self.resolveInst(ty_op.operand); | 4213 | 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)) |
| 4122 | // The MCValue that holds the pointer can be re-used as the value. | 4216 | // The MCValue that holds the pointer can be re-used as the value. |
| 4123 | ptr_mcv | 4217 | ptr_mcv |
| 4124 | else | 4218 | else |
| 4125 | try self.allocRegOrMem(inst, true); | 4219 | try self.allocRegOrMem(inst, true); |
| 4126 | | 4220 | |
| 4127 | const ptr_ty = self.air.typeOf(ty_op.operand); | | |
| 4128 | if (ptr_ty.ptrInfo().data.host_size > 0) { | 4221 | if (ptr_ty.ptrInfo().data.host_size > 0) { |
| 4129 | try self.packedLoad(dst_mcv, ptr_ty, ptr_mcv); | 4222 | try self.packedLoad(dst_mcv, ptr_ty, ptr_mcv); |
| 4130 | } else { | 4223 | } else { |
| ... | @@ -4346,17 +4439,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4346,17 +4439,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 4346 | | 4439 | |
| 4347 | switch (src_mcv) { | 4440 | switch (src_mcv) { |
| 4348 | .load_frame => |frame_addr| { | 4441 | .load_frame => |frame_addr| { |
| 4349 | const field_abi_size = @intCast(u32, field_ty.abiSize(self.target.*)); | 4442 | if (field_off % 8 == 0) { |
| 4350 | const limb_abi_size = @min(field_abi_size, 8); | 4443 | const off_mcv = |
| 4351 | const limb_abi_bits = limb_abi_size * 8; | 4444 | src_mcv.address().offset(@intCast(i32, @divExact(field_off, 8))).deref(); |
| 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 | } }; | | |
| 4360 | if (self.reuseOperand(inst, operand, 0, src_mcv)) break :result off_mcv; | 4445 | if (self.reuseOperand(inst, operand, 0, src_mcv)) break :result off_mcv; |
| 4361 | | 4446 | |
| 4362 | const dst_mcv = try self.allocRegOrMem(inst, true); | 4447 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| ... | @@ -4364,6 +4449,12 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4364,6 +4449,12 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 4364 | break :result dst_mcv; | 4449 | break :result dst_mcv; |
| 4365 | } | 4450 | } |
| 4366 | | 4451 | |
| | 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 | |
| 4367 | if (field_abi_size > 8) { | 4458 | if (field_abi_size > 8) { |
| 4368 | return self.fail("TODO implement struct_field_val with large packed field", .{}); | 4459 | return self.fail("TODO implement struct_field_val with large packed field", .{}); |
| 4369 | } | 4460 | } |
| ... | @@ -5181,24 +5272,69 @@ fn genBinOp( | ... | @@ -5181,24 +5272,69 @@ fn genBinOp( |
| 5181 | switch (tag) { | 5272 | switch (tag) { |
| 5182 | .add, | 5273 | .add, |
| 5183 | .addwrap, | 5274 | .addwrap, |
| 5184 | => try self.genBinOpMir(switch (lhs_ty.tag()) { | 5275 | => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { |
| 5185 | else => .add, | 5276 | else => .add, |
| 5186 | .f32 => .addss, | 5277 | .Float => switch (lhs_ty.floatBits(self.target.*)) { |
| 5187 | .f64 => .addsd, | 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 | }, |
| 5188 | }, lhs_ty, dst_mcv, src_mcv), | 5294 | }, lhs_ty, dst_mcv, src_mcv), |
| 5189 | | 5295 | |
| 5190 | .sub, | 5296 | .sub, |
| 5191 | .subwrap, | 5297 | .subwrap, |
| 5192 | => try self.genBinOpMir(switch (lhs_ty.tag()) { | 5298 | => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { |
| 5193 | else => .sub, | 5299 | else => .sub, |
| 5194 | .f32 => .subss, | 5300 | .Float => switch (lhs_ty.floatBits(self.target.*)) { |
| 5195 | .f64 => .subsd, | 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 | }, |
| 5196 | }, lhs_ty, dst_mcv, src_mcv), | 5317 | }, lhs_ty, dst_mcv, src_mcv), |
| 5197 | | 5318 | |
| 5198 | .mul => try self.genBinOpMir(switch (lhs_ty.tag()) { | 5319 | .mul => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { |
| 5199 | .f32 => .mulss, | | |
| 5200 | .f64 => .mulsd, | | |
| 5201 | else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }), | 5320 | 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 | }, |
| 5202 | }, lhs_ty, dst_mcv, src_mcv), | 5338 | }, lhs_ty, dst_mcv, src_mcv), |
| 5203 | | 5339 | |
| 5204 | .div_float, | 5340 | .div_float, |
| ... | @@ -5206,12 +5342,27 @@ fn genBinOp( | ... | @@ -5206,12 +5342,27 @@ fn genBinOp( |
| 5206 | .div_trunc, | 5342 | .div_trunc, |
| 5207 | .div_floor, | 5343 | .div_floor, |
| 5208 | => { | 5344 | => { |
| 5209 | try self.genBinOpMir(switch (lhs_ty.tag()) { | 5345 | try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { |
| 5210 | .f32 => .divss, | | |
| 5211 | .f64 => .divsd, | | |
| 5212 | else => return self.fail("TODO implement genBinOp for {s} {}", .{ | 5346 | else => return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 5213 | @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), | 5347 | @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), |
| 5214 | }), | 5348 | }), |
| | 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 | }, |
| 5215 | }, lhs_ty, dst_mcv, src_mcv); | 5366 | }, lhs_ty, dst_mcv, src_mcv); |
| 5216 | switch (tag) { | 5367 | switch (tag) { |
| 5217 | .div_float, | 5368 | .div_float, |
| ... | @@ -5222,16 +5373,18 @@ fn genBinOp( | ... | @@ -5222,16 +5373,18 @@ fn genBinOp( |
| 5222 | => if (Target.x86.featureSetHas(self.target.cpu.features, .sse4_1)) { | 5373 | => if (Target.x86.featureSetHas(self.target.cpu.features, .sse4_1)) { |
| 5223 | const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*)); | 5374 | const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*)); |
| 5224 | const dst_alias = registerAlias(dst_mcv.register, abi_size); | 5375 | const dst_alias = registerAlias(dst_mcv.register, abi_size); |
| 5225 | try self.asmRegisterRegisterImmediate(switch (lhs_ty.tag()) { | 5376 | try self.asmRegisterRegisterImmediate(switch (lhs_ty.floatBits(self.target.*)) { |
| 5226 | .f32 => .roundss, | 5377 | 32 => .roundss, |
| 5227 | .f64 => .roundsd, | 5378 | 64 => .roundsd, |
| 5228 | else => unreachable, | 5379 | else => unreachable, |
| 5229 | }, dst_alias, dst_alias, Immediate.u(switch (tag) { | 5380 | }, dst_alias, dst_alias, Immediate.u(switch (tag) { |
| 5230 | .div_trunc => 0b1_0_11, | 5381 | .div_trunc => 0b1_0_11, |
| 5231 | .div_floor => 0b1_0_01, | 5382 | .div_floor => 0b1_0_01, |
| 5232 | else => unreachable, | 5383 | else => unreachable, |
| 5233 | })); | 5384 | })); |
| 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 | }), |
| 5235 | else => unreachable, | 5388 | else => unreachable, |
| 5236 | } | 5389 | } |
| 5237 | }, | 5390 | }, |
| ... | @@ -5453,39 +5606,68 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s | ... | @@ -5453,39 +5606,68 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s |
| 5453 | )), | 5606 | )), |
| 5454 | else => unreachable, | 5607 | else => unreachable, |
| 5455 | }, | 5608 | }, |
| 5456 | .register_offset, | | |
| 5457 | .eflags, | 5609 | .eflags, |
| | 5610 | .register_offset, |
| 5458 | .memory, | 5611 | .memory, |
| | 5612 | .indirect, |
| 5459 | .load_direct, | 5613 | .load_direct, |
| 5460 | .lea_direct, | 5614 | .lea_direct, |
| 5461 | .load_got, | 5615 | .load_got, |
| 5462 | .lea_got, | 5616 | .lea_got, |
| 5463 | .load_tlv, | 5617 | .load_tlv, |
| 5464 | .lea_tlv, | 5618 | .lea_tlv, |
| | 5619 | .load_frame, |
| 5465 | .lea_frame, | 5620 | .lea_frame, |
| 5466 | => { | 5621 | => { |
| 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 | |
| 5468 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); | 5644 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); |
| 5469 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); | 5645 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 5470 | | 5646 | |
| 5471 | const reg = try self.copyToTmpRegister(ty, src_mcv); | 5647 | switch (src_mcv) { |
| 5472 | return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg }); | 5648 | .eflags, |
| 5473 | }, | 5649 | .register_offset, |
| 5474 | .indirect, .load_frame => try self.asmRegisterMemory( | 5650 | .lea_direct, |
| 5475 | mir_tag, | 5651 | .lea_got, |
| 5476 | registerAlias(dst_reg, abi_size), | 5652 | .lea_tlv, |
| 5477 | Memory.sib(Memory.PtrSize.fromSize(abi_size), switch (src_mcv) { | 5653 | .lea_frame, |
| 5478 | .indirect => |reg_off| .{ | 5654 | => { |
| 5479 | .base = .{ .reg = reg_off.reg }, | 5655 | const reg = try self.copyToTmpRegister(ty, src_mcv); |
| 5480 | .disp = reg_off.off, | 5656 | return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg }); |
| 5481 | }, | 5657 | }, |
| 5482 | .load_frame => |frame_addr| .{ | 5658 | .memory, |
| 5483 | .base = .{ .frame = frame_addr.index }, | 5659 | .load_direct, |
| 5484 | .disp = frame_addr.off, | 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 | }); |
| 5485 | }, | 5667 | }, |
| 5486 | else => unreachable, | 5668 | else => unreachable, |
| 5487 | }), | 5669 | } |
| 5488 | ), | 5670 | }, |
| 5489 | } | 5671 | } |
| 5490 | }, | 5672 | }, |
| 5491 | .memory, .indirect, .load_got, .load_direct, .load_tlv, .load_frame => { | 5673 | .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 { | ... | @@ -6175,10 +6357,25 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 6175 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); | 6357 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 6176 | | 6358 | |
| 6177 | const src_mcv = if (flipped) lhs_mcv else rhs_mcv; | 6359 | const src_mcv = if (flipped) lhs_mcv else rhs_mcv; |
| 6178 | try self.genBinOpMir(switch (ty.tag()) { | 6360 | try self.genBinOpMir(switch (ty.zigTypeTag()) { |
| 6179 | else => .cmp, | 6361 | else => .cmp, |
| 6180 | .f32 => .ucomiss, | 6362 | .Float => switch (ty.floatBits(self.target.*)) { |
| 6181 | .f64 => .ucomisd, | 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 | }, |
| 6182 | }, ty, dst_mcv, src_mcv); | 6379 | }, ty, dst_mcv, src_mcv); |
| 6183 | | 6380 | |
| 6184 | const signedness = if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned; | 6381 | 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 { | ... | @@ -7608,7 +7805,8 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { |
| 7608 | const dst_rc = try self.regClassForType(dst_ty); | 7805 | const dst_rc = try self.regClassForType(dst_ty); |
| 7609 | const src_rc = try self.regClassForType(src_ty); | 7806 | const src_rc = try self.regClassForType(src_ty); |
| 7610 | const operand = try self.resolveInst(ty_op.operand); | 7807 | 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; |
| 7612 | | 7810 | |
| 7613 | const operand_lock = switch (operand) { | 7811 | const operand_lock = switch (operand) { |
| 7614 | .register => |reg| self.register_manager.lockReg(reg), | 7812 | .register => |reg| self.register_manager.lockReg(reg), |
| ... | @@ -7648,9 +7846,59 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -7648,9 +7846,59 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 7648 | | 7846 | |
| 7649 | fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { | 7847 | fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { |
| 7650 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 7848 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 7651 | _ = ty_op; | 7849 | |
| 7652 | return self.fail("TODO implement airIntToFloat for {}", .{self.target.cpu.arch}); | 7850 | const src_ty = self.air.typeOf(ty_op.operand); |
| 7653 | //return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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 }); |
| 7654 | } | 7902 | } |
| 7655 | | 7903 | |
| 7656 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { | 7904 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -8717,6 +8965,7 @@ fn resolveCallingConventionValues( | ... | @@ -8717,6 +8965,7 @@ fn resolveCallingConventionValues( |
| 8717 | }, | 8965 | }, |
| 8718 | .C => { | 8966 | .C => { |
| 8719 | var param_reg_i: usize = 0; | 8967 | var param_reg_i: usize = 0; |
| | 8968 | var param_sse_reg_i: usize = 0; |
| 8720 | result.stack_align = 16; | 8969 | result.stack_align = 16; |
| 8721 | | 8970 | |
| 8722 | switch (self.target.os.tag) { | 8971 | switch (self.target.os.tag) { |
| ... | @@ -8734,26 +8983,39 @@ fn resolveCallingConventionValues( | ... | @@ -8734,26 +8983,39 @@ fn resolveCallingConventionValues( |
| 8734 | // TODO: is this even possible for C calling convention? | 8983 | // TODO: is this even possible for C calling convention? |
| 8735 | result.return_value = InstTracking.init(.none); | 8984 | result.return_value = InstTracking.init(.none); |
| 8736 | } else { | 8985 | } else { |
| 8737 | const ret_reg = abi.getCAbiIntReturnRegs(self.target.*)[0]; | 8986 | const classes = switch (self.target.os.tag) { |
| 8738 | const ret_ty_size = @intCast(u31, ret_ty.abiSize(self.target.*)); | 8987 | .windows => &[1]abi.Class{abi.classifyWindows(ret_ty, self.target.*)}, |
| 8739 | if (ret_ty_size <= 8) { | 8988 | else => mem.sliceTo(&abi.classifySystemV(ret_ty, self.target.*, .ret), .none), |
| 8740 | const aliased_reg = registerAlias(ret_reg, ret_ty_size); | 8989 | }; |
| 8741 | result.return_value = .{ .short = .{ .register = aliased_reg }, .long = .none }; | 8990 | if (classes.len > 1) { |
| 8742 | } else { | 8991 | return self.fail("TODO handle multiple classes per type", .{}); |
| 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 | }; | | |
| 8749 | } | 8992 | } |
| | 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 | }; |
| 8750 | } | 9012 | } |
| 8751 | | 9013 | |
| 8752 | // Input params | 9014 | // Input params |
| 8753 | for (param_types, result.args) |ty, *arg| { | 9015 | for (param_types, result.args) |ty, *arg| { |
| 8754 | assert(ty.hasRuntimeBitsIgnoreComptime()); | 9016 | assert(ty.hasRuntimeBitsIgnoreComptime()); |
| 8755 | | 9017 | |
| 8756 | const classes: []const abi.Class = switch (self.target.os.tag) { | 9018 | const classes = switch (self.target.os.tag) { |
| 8757 | .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)}, | 9019 | .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)}, |
| 8758 | else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*, .arg), .none), | 9020 | else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*, .arg), .none), |
| 8759 | }; | 9021 | }; |
| ... | @@ -8761,13 +9023,29 @@ fn resolveCallingConventionValues( | ... | @@ -8761,13 +9023,29 @@ fn resolveCallingConventionValues( |
| 8761 | return self.fail("TODO handle multiple classes per type", .{}); | 9023 | return self.fail("TODO handle multiple classes per type", .{}); |
| 8762 | } | 9024 | } |
| 8763 | switch (classes[0]) { | 9025 | switch (classes[0]) { |
| 8764 | .integer => blk: { | 9026 | .integer => if (param_reg_i < abi.getCAbiIntParamRegs(self.target.*).len) { |
| 8765 | if (param_reg_i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; | 9027 | arg.* = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i] }; |
| 8766 | const param_reg = abi.getCAbiIntParamRegs(self.target.*)[param_reg_i]; | | |
| 8767 | param_reg_i += 1; | 9028 | param_reg_i += 1; |
| 8768 | arg.* = .{ .register = param_reg }; | | |
| 8769 | continue; | 9029 | continue; |
| 8770 | }, | 9030 | }, |
| | 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 | }, |
| 8771 | .memory => {}, // fallthrough | 9049 | .memory => {}, // fallthrough |
| 8772 | else => |class| return self.fail("TODO handle calling convention class {s}", .{ | 9050 | else => |class| return self.fail("TODO handle calling convention class {s}", .{ |
| 8773 | @tagName(class), | 9051 | @tagName(class), |