| ... | ... | @@ -1297,9 +1297,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 1297 | 1297 | .ceil, |
| 1298 | 1298 | .round, |
| 1299 | 1299 | .trunc_float, |
| 1300 | | .neg, |
| 1301 | 1300 | => try self.airUnaryMath(inst), |
| 1302 | 1301 | |
| 1302 | .neg => try self.airNeg(inst), |
| 1303 | |
| 1303 | 1304 | .add_with_overflow => try self.airAddSubWithOverflow(inst), |
| 1304 | 1305 | .sub_with_overflow => try self.airAddSubWithOverflow(inst), |
| 1305 | 1306 | .mul_with_overflow => try self.airMulWithOverflow(inst), |
| ... | ... | @@ -1881,7 +1882,7 @@ pub fn spillRegisters(self: *Self, registers: []const Register) !void { |
| 1881 | 1882 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 1882 | 1883 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 1883 | 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 | 1886 | try self.genSetReg(reg, ty, mcv); |
| 1886 | 1887 | return reg; |
| 1887 | 1888 | } |
| ... | ... | @@ -1924,16 +1925,48 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1924 | 1925 | |
| 1925 | 1926 | fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { |
| 1926 | 1927 | 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 }); |
| 1930 | 1947 | } |
| 1931 | 1948 | |
| 1932 | 1949 | fn airFpext(self: *Self, inst: Air.Inst.Index) !void { |
| 1933 | 1950 | 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 }); |
| 1937 | 1970 | } |
| 1938 | 1971 | |
| 1939 | 1972 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -3953,10 +3986,65 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { |
| 3953 | 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 | 4042 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 3957 | 4043 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 3958 | 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 | 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 | 4197 | fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 4110 | 4198 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 4111 | 4199 | const elem_ty = self.air.typeOfIndex(inst); |
| 4112 | | const elem_size = elem_ty.abiSize(self.target.*); |
| 4113 | 4200 | const result: MCValue = result: { |
| 4114 | 4201 | if (!elem_ty.hasRuntimeBitsIgnoreComptime()) break :result .none; |
| 4115 | 4202 | |
| ... | ... | @@ -4117,14 +4204,20 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 4117 | 4204 | const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); |
| 4118 | 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 | 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 | 4216 | // The MCValue that holds the pointer can be re-used as the value. |
| 4123 | 4217 | ptr_mcv |
| 4124 | 4218 | else |
| 4125 | 4219 | try self.allocRegOrMem(inst, true); |
| 4126 | 4220 | |
| 4127 | | const ptr_ty = self.air.typeOf(ty_op.operand); |
| 4128 | 4221 | if (ptr_ty.ptrInfo().data.host_size > 0) { |
| 4129 | 4222 | try self.packedLoad(dst_mcv, ptr_ty, ptr_mcv); |
| 4130 | 4223 | } else { |
| ... | ... | @@ -4346,17 +4439,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 4346 | 4439 | |
| 4347 | 4440 | switch (src_mcv) { |
| 4348 | 4441 | .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(); |
| 4360 | 4445 | if (self.reuseOperand(inst, operand, 0, src_mcv)) break :result off_mcv; |
| 4361 | 4446 | |
| 4362 | 4447 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| ... | ... | @@ -4364,6 +4449,12 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 4364 | 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 | 4458 | if (field_abi_size > 8) { |
| 4368 | 4459 | return self.fail("TODO implement struct_field_val with large packed field", .{}); |
| 4369 | 4460 | } |
| ... | ... | @@ -5181,24 +5272,69 @@ fn genBinOp( |
| 5181 | 5272 | switch (tag) { |
| 5182 | 5273 | .add, |
| 5183 | 5274 | .addwrap, |
| 5184 | | => try self.genBinOpMir(switch (lhs_ty.tag()) { |
| 5275 | => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { |
| 5185 | 5276 | 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 | }, |
| 5188 | 5294 | }, lhs_ty, dst_mcv, src_mcv), |
| 5189 | 5295 | |
| 5190 | 5296 | .sub, |
| 5191 | 5297 | .subwrap, |
| 5192 | | => try self.genBinOpMir(switch (lhs_ty.tag()) { |
| 5298 | => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { |
| 5193 | 5299 | 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 | }, |
| 5196 | 5317 | }, lhs_ty, dst_mcv, src_mcv), |
| 5197 | 5318 | |
| 5198 | | .mul => try self.genBinOpMir(switch (lhs_ty.tag()) { |
| 5199 | | .f32 => .mulss, |
| 5200 | | .f64 => .mulsd, |
| 5319 | .mul => try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { |
| 5201 | 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 | 5338 | }, lhs_ty, dst_mcv, src_mcv), |
| 5203 | 5339 | |
| 5204 | 5340 | .div_float, |
| ... | ... | @@ -5206,12 +5342,27 @@ fn genBinOp( |
| 5206 | 5342 | .div_trunc, |
| 5207 | 5343 | .div_floor, |
| 5208 | 5344 | => { |
| 5209 | | try self.genBinOpMir(switch (lhs_ty.tag()) { |
| 5210 | | .f32 => .divss, |
| 5211 | | .f64 => .divsd, |
| 5345 | try self.genBinOpMir(switch (lhs_ty.zigTypeTag()) { |
| 5212 | 5346 | else => return self.fail("TODO implement genBinOp for {s} {}", .{ |
| 5213 | 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 | 5366 | }, lhs_ty, dst_mcv, src_mcv); |
| 5216 | 5367 | switch (tag) { |
| 5217 | 5368 | .div_float, |
| ... | ... | @@ -5222,16 +5373,18 @@ fn genBinOp( |
| 5222 | 5373 | => if (Target.x86.featureSetHas(self.target.cpu.features, .sse4_1)) { |
| 5223 | 5374 | const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*)); |
| 5224 | 5375 | 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, |
| 5228 | 5379 | else => unreachable, |
| 5229 | 5380 | }, dst_alias, dst_alias, Immediate.u(switch (tag) { |
| 5230 | 5381 | .div_trunc => 0b1_0_11, |
| 5231 | 5382 | .div_floor => 0b1_0_01, |
| 5232 | 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 | 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 | 5606 | )), |
| 5454 | 5607 | else => unreachable, |
| 5455 | 5608 | }, |
| 5456 | | .register_offset, |
| 5457 | 5609 | .eflags, |
| 5610 | .register_offset, |
| 5458 | 5611 | .memory, |
| 5612 | .indirect, |
| 5459 | 5613 | .load_direct, |
| 5460 | 5614 | .lea_direct, |
| 5461 | 5615 | .load_got, |
| 5462 | 5616 | .lea_got, |
| 5463 | 5617 | .load_tlv, |
| 5464 | 5618 | .lea_tlv, |
| 5619 | .load_frame, |
| 5465 | 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 | 5644 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); |
| 5469 | 5645 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 5470 | 5646 | |
| 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 }); |
| 5481 | 5657 | }, |
| 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 | }); |
| 5485 | 5667 | }, |
| 5486 | 5668 | else => unreachable, |
| 5487 | | }), |
| 5488 | | ), |
| 5669 | } |
| 5670 | }, |
| 5489 | 5671 | } |
| 5490 | 5672 | }, |
| 5491 | 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 | 6357 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 6176 | 6358 | |
| 6177 | 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 | 6361 | 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 | }, |
| 6182 | 6379 | }, ty, dst_mcv, src_mcv); |
| 6183 | 6380 | |
| 6184 | 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 | 7805 | const dst_rc = try self.regClassForType(dst_ty); |
| 7609 | 7806 | const src_rc = try self.regClassForType(src_ty); |
| 7610 | 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 | 7811 | const operand_lock = switch (operand) { |
| 7614 | 7812 | .register => |reg| self.register_manager.lockReg(reg), |
| ... | ... | @@ -7648,9 +7846,59 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { |
| 7648 | 7846 | |
| 7649 | 7847 | fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { |
| 7650 | 7848 | 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 }); |
| 7654 | 7902 | } |
| 7655 | 7903 | |
| 7656 | 7904 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -8717,6 +8965,7 @@ fn resolveCallingConventionValues( |
| 8717 | 8965 | }, |
| 8718 | 8966 | .C => { |
| 8719 | 8967 | var param_reg_i: usize = 0; |
| 8968 | var param_sse_reg_i: usize = 0; |
| 8720 | 8969 | result.stack_align = 16; |
| 8721 | 8970 | |
| 8722 | 8971 | switch (self.target.os.tag) { |
| ... | ... | @@ -8734,26 +8983,39 @@ fn resolveCallingConventionValues( |
| 8734 | 8983 | // TODO: is this even possible for C calling convention? |
| 8735 | 8984 | result.return_value = InstTracking.init(.none); |
| 8736 | 8985 | } 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", .{}); |
| 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 | 9014 | // Input params |
| 8753 | 9015 | for (param_types, result.args) |ty, *arg| { |
| 8754 | 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 | 9019 | .windows => &[1]abi.Class{abi.classifyWindows(ty, self.target.*)}, |
| 8758 | 9020 | else => mem.sliceTo(&abi.classifySystemV(ty, self.target.*, .arg), .none), |
| 8759 | 9021 | }; |
| ... | ... | @@ -8761,13 +9023,29 @@ fn resolveCallingConventionValues( |
| 8761 | 9023 | return self.fail("TODO handle multiple classes per type", .{}); |
| 8762 | 9024 | } |
| 8763 | 9025 | 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] }; |
| 8767 | 9028 | param_reg_i += 1; |
| 8768 | | arg.* = .{ .register = param_reg }; |
| 8769 | 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 | 9049 | .memory => {}, // fallthrough |
| 8772 | 9050 | else => |class| return self.fail("TODO handle calling convention class {s}", .{ |
| 8773 | 9051 | @tagName(class), |