| ... | ... | @@ -39,7 +39,7 @@ const RegisterLock = RegisterManager.RegisterLock; |
| 39 | 39 | const Register = bits.Register; |
| 40 | 40 | |
| 41 | 41 | const gp = abi.RegisterClass.gp; |
| 42 | | const avx = abi.RegisterClass.avx; |
| 42 | const sse = abi.RegisterClass.sse; |
| 43 | 43 | |
| 44 | 44 | const InnerError = error{ |
| 45 | 45 | OutOfMemory, |
| ... | ... | @@ -881,15 +881,18 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 881 | 881 | switch (elem_ty.zigTypeTag()) { |
| 882 | 882 | .Vector => return self.fail("TODO allocRegOrMem for Vector type", .{}), |
| 883 | 883 | .Float => { |
| 884 | | // TODO check if AVX available |
| 885 | | const ptr_bytes: u64 = 32; |
| 886 | | if (abi_size <= ptr_bytes) { |
| 887 | | if (self.register_manager.tryAllocReg(inst, .{ |
| 888 | | .selector_mask = avx, |
| 889 | | })) |reg| { |
| 890 | | return MCValue{ .register = registerAlias(reg, abi_size) }; |
| 884 | if (self.intrinsicsAllowed(elem_ty)) { |
| 885 | const ptr_bytes: u64 = 32; |
| 886 | if (abi_size <= ptr_bytes) { |
| 887 | if (self.register_manager.tryAllocReg(inst, .{ |
| 888 | .selector_mask = sse, |
| 889 | })) |reg| { |
| 890 | return MCValue{ .register = registerAlias(reg, abi_size) }; |
| 891 | } |
| 891 | 892 | } |
| 892 | 893 | } |
| 894 | |
| 895 | return self.fail("TODO allocRegOrMem for Float type without SSE/AVX support", .{}); |
| 893 | 896 | }, |
| 894 | 897 | else => { |
| 895 | 898 | // Make sure the type can fit in a register before we try to allocate one. |
| ... | ... | @@ -969,8 +972,11 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou |
| 969 | 972 | /// allocated. A second call to `copyToTmpRegister` may return the same register. |
| 970 | 973 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 971 | 974 | fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 972 | | const mask = switch (ty.zigTypeTag()) { |
| 973 | | .Float => avx, |
| 975 | const mask: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { |
| 976 | .Float => blk: { |
| 977 | if (self.intrinsicsAllowed(ty)) break :blk sse; |
| 978 | return self.fail("TODO copy {} to register", .{ty.fmtDebug()}); |
| 979 | }, |
| 974 | 980 | else => gp, |
| 975 | 981 | }; |
| 976 | 982 | const reg: Register = try self.register_manager.allocReg(null, .{ |
| ... | ... | @@ -985,8 +991,11 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { |
| 985 | 991 | /// This can have a side effect of spilling instructions to the stack to free up a register. |
| 986 | 992 | /// WARNING make sure that the allocated register matches the returned MCValue from an instruction! |
| 987 | 993 | fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue { |
| 988 | | const mask = switch (ty.zigTypeTag()) { |
| 989 | | .Float => avx, |
| 994 | const mask: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) { |
| 995 | .Float => blk: { |
| 996 | if (self.intrinsicsAllowed(ty)) break :blk sse; |
| 997 | return self.fail("TODO copy {} to register", .{ty.fmtDebug()}); |
| 998 | }, |
| 990 | 999 | else => gp, |
| 991 | 1000 | }; |
| 992 | 1001 | const reg: Register = try self.register_manager.allocReg(reg_owner, .{ |
| ... | ... | @@ -3469,27 +3478,32 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 3469 | 3478 | }, |
| 3470 | 3479 | .register => |src_reg| switch (dst_ty.zigTypeTag()) { |
| 3471 | 3480 | .Float => { |
| 3472 | | const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { |
| 3473 | | .f32 => switch (mir_tag) { |
| 3474 | | .add => Mir.Inst.Tag.add_f32, |
| 3475 | | .cmp => Mir.Inst.Tag.cmp_f32, |
| 3476 | | else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), |
| 3477 | | }, |
| 3478 | | .f64 => switch (mir_tag) { |
| 3479 | | .add => Mir.Inst.Tag.add_f64, |
| 3480 | | .cmp => Mir.Inst.Tag.cmp_f64, |
| 3481 | | else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), |
| 3482 | | }, |
| 3483 | | else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), |
| 3484 | | }; |
| 3485 | | _ = try self.addInst(.{ |
| 3486 | | .tag = actual_tag, |
| 3487 | | .ops = Mir.Inst.Ops.encode(.{ |
| 3488 | | .reg1 = dst_reg.to128(), |
| 3489 | | .reg2 = src_reg.to128(), |
| 3490 | | }), |
| 3491 | | .data = undefined, |
| 3492 | | }); |
| 3481 | if (self.intrinsicsAllowed(dst_ty)) { |
| 3482 | const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) { |
| 3483 | .f32 => switch (mir_tag) { |
| 3484 | .add => Mir.Inst.Tag.add_f32_avx, |
| 3485 | .cmp => Mir.Inst.Tag.cmp_f32_avx, |
| 3486 | else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}), |
| 3487 | }, |
| 3488 | .f64 => switch (mir_tag) { |
| 3489 | .add => Mir.Inst.Tag.add_f64_avx, |
| 3490 | .cmp => Mir.Inst.Tag.cmp_f64_avx, |
| 3491 | else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}), |
| 3492 | }, |
| 3493 | else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}), |
| 3494 | }; |
| 3495 | _ = try self.addInst(.{ |
| 3496 | .tag = actual_tag, |
| 3497 | .ops = Mir.Inst.Ops.encode(.{ |
| 3498 | .reg1 = dst_reg.to128(), |
| 3499 | .reg2 = src_reg.to128(), |
| 3500 | }), |
| 3501 | .data = undefined, |
| 3502 | }); |
| 3503 | return; |
| 3504 | } |
| 3505 | |
| 3506 | return self.fail("TODO genBinOpMir for float register-register and no intrinsics", .{}); |
| 3493 | 3507 | }, |
| 3494 | 3508 | else => { |
| 3495 | 3509 | _ = try self.addInst(.{ |
| ... | ... | @@ -5326,24 +5340,29 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 5326 | 5340 | .register => |reg| { |
| 5327 | 5341 | switch (ty.zigTypeTag()) { |
| 5328 | 5342 | .Float => { |
| 5329 | | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 5330 | | .f32 => .mov_f32, |
| 5331 | | .f64 => .mov_f64, |
| 5332 | | else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), |
| 5333 | | }; |
| 5334 | | _ = try self.addInst(.{ |
| 5335 | | .tag = tag, |
| 5336 | | .ops = Mir.Inst.Ops.encode(.{ |
| 5337 | | .reg1 = switch (ty.tag()) { |
| 5338 | | .f32 => .esp, |
| 5339 | | .f64 => .rsp, |
| 5340 | | else => unreachable, |
| 5341 | | }, |
| 5342 | | .reg2 = reg.to128(), |
| 5343 | | .flags = 0b01, |
| 5344 | | }), |
| 5345 | | .data = .{ .imm = @bitCast(u32, -stack_offset) }, |
| 5346 | | }); |
| 5343 | if (self.intrinsicsAllowed(ty)) { |
| 5344 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 5345 | .f32 => .mov_f32_avx, |
| 5346 | .f64 => .mov_f64_avx, |
| 5347 | else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}), |
| 5348 | }; |
| 5349 | _ = try self.addInst(.{ |
| 5350 | .tag = tag, |
| 5351 | .ops = Mir.Inst.Ops.encode(.{ |
| 5352 | .reg1 = switch (ty.tag()) { |
| 5353 | .f32 => .esp, |
| 5354 | .f64 => .rsp, |
| 5355 | else => unreachable, |
| 5356 | }, |
| 5357 | .reg2 = reg.to128(), |
| 5358 | .flags = 0b01, |
| 5359 | }), |
| 5360 | .data = .{ .imm = @bitCast(u32, -stack_offset) }, |
| 5361 | }); |
| 5362 | return; |
| 5363 | } |
| 5364 | |
| 5365 | return self.fail("TODO genSetStackArg for register with no intrinsics", .{}); |
| 5347 | 5366 | }, |
| 5348 | 5367 | else => { |
| 5349 | 5368 | _ = try self.addInst(.{ |
| ... | ... | @@ -5505,24 +5524,29 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 5505 | 5524 | |
| 5506 | 5525 | switch (ty.zigTypeTag()) { |
| 5507 | 5526 | .Float => { |
| 5508 | | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 5509 | | .f32 => .mov_f32, |
| 5510 | | .f64 => .mov_f64, |
| 5511 | | else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), |
| 5512 | | }; |
| 5513 | | _ = try self.addInst(.{ |
| 5514 | | .tag = tag, |
| 5515 | | .ops = Mir.Inst.Ops.encode(.{ |
| 5516 | | .reg1 = switch (ty.tag()) { |
| 5517 | | .f32 => base_reg.to32(), |
| 5518 | | .f64 => base_reg.to64(), |
| 5519 | | else => unreachable, |
| 5520 | | }, |
| 5521 | | .reg2 = reg.to128(), |
| 5522 | | .flags = 0b01, |
| 5523 | | }), |
| 5524 | | .data = .{ .imm = @bitCast(u32, -stack_offset) }, |
| 5525 | | }); |
| 5527 | if (self.intrinsicsAllowed(ty)) { |
| 5528 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 5529 | .f32 => .mov_f32_avx, |
| 5530 | .f64 => .mov_f64_avx, |
| 5531 | else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}), |
| 5532 | }; |
| 5533 | _ = try self.addInst(.{ |
| 5534 | .tag = tag, |
| 5535 | .ops = Mir.Inst.Ops.encode(.{ |
| 5536 | .reg1 = switch (ty.tag()) { |
| 5537 | .f32 => base_reg.to32(), |
| 5538 | .f64 => base_reg.to64(), |
| 5539 | else => unreachable, |
| 5540 | }, |
| 5541 | .reg2 = reg.to128(), |
| 5542 | .flags = 0b01, |
| 5543 | }), |
| 5544 | .data = .{ .imm = @bitCast(u32, -stack_offset) }, |
| 5545 | }); |
| 5546 | return; |
| 5547 | } |
| 5548 | |
| 5549 | return self.fail("TODO genSetStack for register for type float with no intrinsics", .{}); |
| 5526 | 5550 | }, |
| 5527 | 5551 | else => { |
| 5528 | 5552 | if (!math.isPowerOfTwo(abi_size)) { |
| ... | ... | @@ -6026,21 +6050,25 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6026 | 6050 | }, |
| 6027 | 6051 | }, |
| 6028 | 6052 | .Float => { |
| 6029 | | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6030 | | .f32 => .mov_f32, |
| 6031 | | .f64 => .mov_f64, |
| 6032 | | else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), |
| 6033 | | }; |
| 6034 | | _ = try self.addInst(.{ |
| 6035 | | .tag = tag, |
| 6036 | | .ops = Mir.Inst.Ops.encode(.{ |
| 6037 | | .reg1 = reg.to128(), |
| 6038 | | .reg2 = src_reg.to128(), |
| 6039 | | .flags = 0b10, |
| 6040 | | }), |
| 6041 | | .data = undefined, |
| 6042 | | }); |
| 6043 | | return; |
| 6053 | if (self.intrinsicsAllowed(ty)) { |
| 6054 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6055 | .f32 => .mov_f32_avx, |
| 6056 | .f64 => .mov_f64_avx, |
| 6057 | else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}), |
| 6058 | }; |
| 6059 | _ = try self.addInst(.{ |
| 6060 | .tag = tag, |
| 6061 | .ops = Mir.Inst.Ops.encode(.{ |
| 6062 | .reg1 = reg.to128(), |
| 6063 | .reg2 = src_reg.to128(), |
| 6064 | .flags = 0b10, |
| 6065 | }), |
| 6066 | .data = undefined, |
| 6067 | }); |
| 6068 | return; |
| 6069 | } |
| 6070 | |
| 6071 | return self.fail("TODO genSetReg from register for float with no intrinsics", .{}); |
| 6044 | 6072 | }, |
| 6045 | 6073 | else => {}, |
| 6046 | 6074 | } |
| ... | ... | @@ -6073,24 +6101,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6073 | 6101 | const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp }); |
| 6074 | 6102 | try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv); |
| 6075 | 6103 | |
| 6076 | | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6077 | | .f32 => .mov_f32, |
| 6078 | | .f64 => .mov_f64, |
| 6079 | | else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), |
| 6080 | | }; |
| 6104 | if (self.intrinsicsAllowed(ty)) { |
| 6105 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6106 | .f32 => .mov_f32_avx, |
| 6107 | .f64 => .mov_f64_avx, |
| 6108 | else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}), |
| 6109 | }; |
| 6081 | 6110 | |
| 6082 | | _ = try self.addInst(.{ |
| 6083 | | .tag = tag, |
| 6084 | | .ops = Mir.Inst.Ops.encode(.{ |
| 6085 | | .reg1 = reg.to128(), |
| 6086 | | .reg2 = switch (ty.tag()) { |
| 6087 | | .f32 => base_reg.to32(), |
| 6088 | | .f64 => base_reg.to64(), |
| 6089 | | else => unreachable, |
| 6090 | | }, |
| 6091 | | }), |
| 6092 | | .data = .{ .imm = 0 }, |
| 6093 | | }); |
| 6111 | _ = try self.addInst(.{ |
| 6112 | .tag = tag, |
| 6113 | .ops = Mir.Inst.Ops.encode(.{ |
| 6114 | .reg1 = reg.to128(), |
| 6115 | .reg2 = switch (ty.tag()) { |
| 6116 | .f32 => base_reg.to32(), |
| 6117 | .f64 => base_reg.to64(), |
| 6118 | else => unreachable, |
| 6119 | }, |
| 6120 | }), |
| 6121 | .data = .{ .imm = 0 }, |
| 6122 | }); |
| 6123 | return; |
| 6124 | } |
| 6125 | |
| 6126 | return self.fail("TODO genSetReg from memory for float with no intrinsics", .{}); |
| 6094 | 6127 | }, |
| 6095 | 6128 | else => { |
| 6096 | 6129 | if (x <= math.maxInt(i32)) { |
| ... | ... | @@ -6183,24 +6216,27 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6183 | 6216 | }, |
| 6184 | 6217 | }, |
| 6185 | 6218 | .Float => { |
| 6186 | | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6187 | | .f32 => .mov_f32, |
| 6188 | | .f64 => .mov_f64, |
| 6189 | | else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), |
| 6190 | | }; |
| 6191 | | _ = try self.addInst(.{ |
| 6192 | | .tag = tag, |
| 6193 | | .ops = Mir.Inst.Ops.encode(.{ |
| 6194 | | .reg1 = reg.to128(), |
| 6195 | | .reg2 = switch (ty.tag()) { |
| 6196 | | .f32 => .ebp, |
| 6197 | | .f64 => .rbp, |
| 6198 | | else => unreachable, |
| 6199 | | }, |
| 6200 | | }), |
| 6201 | | .data = .{ .imm = @bitCast(u32, -off) }, |
| 6202 | | }); |
| 6203 | | return; |
| 6219 | if (self.intrinsicsAllowed(ty)) { |
| 6220 | const tag: Mir.Inst.Tag = switch (ty.tag()) { |
| 6221 | .f32 => .mov_f32_avx, |
| 6222 | .f64 => .mov_f64_avx, |
| 6223 | else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}), |
| 6224 | }; |
| 6225 | _ = try self.addInst(.{ |
| 6226 | .tag = tag, |
| 6227 | .ops = Mir.Inst.Ops.encode(.{ |
| 6228 | .reg1 = reg.to128(), |
| 6229 | .reg2 = switch (ty.tag()) { |
| 6230 | .f32 => .ebp, |
| 6231 | .f64 => .rbp, |
| 6232 | else => unreachable, |
| 6233 | }, |
| 6234 | }), |
| 6235 | .data = .{ .imm = @bitCast(u32, -off) }, |
| 6236 | }); |
| 6237 | return; |
| 6238 | } |
| 6239 | return self.fail("TODO genSetReg from stack offset for float with no intrinsics", .{}); |
| 6204 | 6240 | }, |
| 6205 | 6241 | else => {}, |
| 6206 | 6242 | } |
| ... | ... | @@ -6995,3 +7031,12 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { |
| 6995 | 7031 | }, |
| 6996 | 7032 | } |
| 6997 | 7033 | } |
| 7034 | |
| 7035 | fn intrinsicsAllowed(self: *Self, ty: Type) bool { |
| 7036 | return switch (ty.tag()) { |
| 7037 | .f32, |
| 7038 | .f64, |
| 7039 | => Target.x86.featureSetHasAny(self.target.cpu.features, .{ .avx, .avx2 }), |
| 7040 | else => unreachable, // TODO finish this off |
| 7041 | }; |
| 7042 | } |