| author | |
| committer | |
| log | d71bd0300bb4dd7f1f90c1e517d197c71abeb277 |
| tree | 89f567182eafa6e41ea38ef4319c9f3758caeef4 |
| parent | a9b6de693ce04f73f8aecce91e8033951024c123 |
| parent | 355d0d0e7e061c86fbc08247f49900488e98acd1 |
| signature |
stage2: x86_64: implement `@floatToInt` for `f32` and `f64`4 files changed, 124 insertions(+), 6 deletions(-)
src/arch/x86_64/CodeGen.zig+50-5| ... | @@ -5481,11 +5481,56 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5481,11 +5481,56 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { |
| 5481 | 5481 | ||
| 5482 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { | 5482 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 5483 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 5483 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 5484 | const result: MCValue = if (self.liveness.isUnused(inst)) | 5484 | if (self.liveness.isUnused(inst)) |
| 5485 | .dead | 5485 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| 5486 | else | 5486 | |
| 5487 | return self.fail("TODO implement airFloatToInt for {}", .{self.target.cpu.arch}); | 5487 | const src_ty = self.air.typeOf(ty_op.operand); |
| 5488 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 5488 | const dst_ty = self.air.typeOfIndex(inst); |
| 5489 | const operand = try self.resolveInst(ty_op.operand); | ||
| 5490 | |||
| 5491 | // move float src to ST(0) | ||
| 5492 | const stack_offset = switch (operand) { | ||
| 5493 | .stack_offset, .ptr_stack_offset => |offset| offset, | ||
| 5494 | else => blk: { | ||
| 5495 | const offset = @intCast(i32, try self.allocMem( | ||
| 5496 | inst, | ||
| 5497 | @intCast(u32, src_ty.abiSize(self.target.*)), | ||
| 5498 | src_ty.abiAlignment(self.target.*), | ||
| 5499 | )); | ||
| 5500 | try self.genSetStack(src_ty, offset, operand, .{}); | ||
| 5501 | break :blk offset; | ||
| 5502 | }, | ||
| 5503 | }; | ||
| 5504 | _ = try self.addInst(.{ | ||
| 5505 | .tag = .fld, | ||
| 5506 | .ops = (Mir.Ops{ | ||
| 5507 | .flags = switch (src_ty.abiSize(self.target.*)) { | ||
| 5508 | 4 => 0b01, | ||
| 5509 | 8 => 0b10, | ||
| 5510 | else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), | ||
| 5511 | }, | ||
| 5512 | .reg1 = .rbp, | ||
| 5513 | }).encode(), | ||
| 5514 | .data = .{ .imm = @bitCast(u32, -stack_offset) }, | ||
| 5515 | }); | ||
| 5516 | |||
| 5517 | // convert | ||
| 5518 | const stack_dst = try self.allocRegOrMem(inst, false); | ||
| 5519 | _ = try self.addInst(.{ | ||
| 5520 | .tag = .fisttp, | ||
| 5521 | .ops = (Mir.Ops{ | ||
| 5522 | .flags = switch (dst_ty.abiSize(self.target.*)) { | ||
| 5523 | 1...2 => 0b00, | ||
| 5524 | 3...4 => 0b01, | ||
| 5525 | 5...8 => 0b10, | ||
| 5526 | else => |size| return self.fail("TODO convert float with abiSize={}", .{size}), | ||
| 5527 | }, | ||
| 5528 | .reg1 = .rbp, | ||
| 5529 | }).encode(), | ||
| 5530 | .data = .{ .imm = @bitCast(u32, -stack_dst.stack_offset) }, | ||
| 5531 | }); | ||
| 5532 | |||
| 5533 | return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none }); | ||
| 5489 | } | 5534 | } |
| 5490 | 5535 | ||
| 5491 | fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { | 5536 | fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
src/arch/x86_64/Emit.zig+60| ... | @@ -131,6 +131,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void { | ... | @@ -131,6 +131,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void { |
| 131 | 131 | ||
| 132 | .movabs => try emit.mirMovabs(inst), | 132 | .movabs => try emit.mirMovabs(inst), |
| 133 | 133 | ||
| 134 | .fisttp => try emit.mirFisttp(inst), | ||
| 135 | .fld => try emit.mirFld(inst), | ||
| 136 | |||
| 134 | .lea => try emit.mirLea(inst), | 137 | .lea => try emit.mirLea(inst), |
| 135 | .lea_pie => try emit.mirLeaPie(inst), | 138 | .lea_pie => try emit.mirLeaPie(inst), |
| 136 | 139 | ||
| ... | @@ -686,6 +689,48 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -686,6 +689,48 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 686 | return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); | 689 | return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); |
| 687 | } | 690 | } |
| 688 | 691 | ||
| 692 | fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ||
| 693 | const tag = emit.mir.instructions.items(.tag)[inst]; | ||
| 694 | assert(tag == .fisttp); | ||
| 695 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); | ||
| 696 | |||
| 697 | // the selecting between operand sizes for this particular `fisttp` instruction | ||
| 698 | // is done via opcode instead of the usual prefixes. | ||
| 699 | |||
| 700 | const opcode: Tag = switch (ops.flags) { | ||
| 701 | 0b00 => .fisttp16, | ||
| 702 | 0b01 => .fisttp32, | ||
| 703 | 0b10 => .fisttp64, | ||
| 704 | else => unreachable, | ||
| 705 | }; | ||
| 706 | const mem_or_reg = Memory{ | ||
| 707 | .base = ops.reg1, | ||
| 708 | .disp = emit.mir.instructions.items(.data)[inst].imm, | ||
| 709 | .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used | ||
| 710 | }; | ||
| 711 | return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); | ||
| 712 | } | ||
| 713 | |||
| 714 | fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ||
| 715 | const tag = emit.mir.instructions.items(.tag)[inst]; | ||
| 716 | assert(tag == .fld); | ||
| 717 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); | ||
| 718 | |||
| 719 | // the selecting between operand sizes for this particular `fisttp` instruction | ||
| 720 | // is done via opcode instead of the usual prefixes. | ||
| 721 | |||
| 722 | const opcode: Tag = switch (ops.flags) { | ||
| 723 | 0b01 => .fld32, | ||
| 724 | 0b10 => .fld64, | ||
| 725 | else => unreachable, | ||
| 726 | }; | ||
| 727 | const mem_or_reg = Memory{ | ||
| 728 | .base = ops.reg1, | ||
| 729 | .disp = emit.mir.instructions.items(.data)[inst].imm, | ||
| 730 | .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used | ||
| 731 | }; | ||
| 732 | return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); | ||
| 733 | } | ||
| 689 | fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | 734 | fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 690 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); | 735 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 691 | switch (ops.flags) { | 736 | switch (ops.flags) { |
| ... | @@ -1114,6 +1159,11 @@ const Tag = enum { | ... | @@ -1114,6 +1159,11 @@ const Tag = enum { |
| 1114 | syscall, | 1159 | syscall, |
| 1115 | ret_near, | 1160 | ret_near, |
| 1116 | ret_far, | 1161 | ret_far, |
| 1162 | fisttp16, | ||
| 1163 | fisttp32, | ||
| 1164 | fisttp64, | ||
| 1165 | fld32, | ||
| 1166 | fld64, | ||
| 1117 | jo, | 1167 | jo, |
| 1118 | jno, | 1168 | jno, |
| 1119 | jb, | 1169 | jb, |
| ... | @@ -1352,6 +1402,11 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { | ... | @@ -1352,6 +1402,11 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { |
| 1352 | .setle, .setng => OpCode.twoByte(0x0f, 0x9e), | 1402 | .setle, .setng => OpCode.twoByte(0x0f, 0x9e), |
| 1353 | .setnle, .setg => OpCode.twoByte(0x0f, 0x9f), | 1403 | .setnle, .setg => OpCode.twoByte(0x0f, 0x9f), |
| 1354 | .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), | 1404 | .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), |
| 1405 | .fisttp16 => OpCode.oneByte(0xdf), | ||
| 1406 | .fisttp32 => OpCode.oneByte(0xdb), | ||
| 1407 | .fisttp64 => OpCode.oneByte(0xdd), | ||
| 1408 | .fld32 => OpCode.oneByte(0xd9), | ||
| 1409 | .fld64 => OpCode.oneByte(0xdd), | ||
| 1355 | else => null, | 1410 | else => null, |
| 1356 | }, | 1411 | }, |
| 1357 | .o => return switch (tag) { | 1412 | .o => return switch (tag) { |
| ... | @@ -1492,6 +1547,11 @@ inline fn getModRmExt(tag: Tag) ?u3 { | ... | @@ -1492,6 +1547,11 @@ inline fn getModRmExt(tag: Tag) ?u3 { |
| 1492 | .imul => 0x5, | 1547 | .imul => 0x5, |
| 1493 | .idiv => 0x7, | 1548 | .idiv => 0x7, |
| 1494 | .div => 0x6, | 1549 | .div => 0x6, |
| 1550 | .fisttp16 => 0x1, | ||
| 1551 | .fisttp32 => 0x1, | ||
| 1552 | .fisttp64 => 0x1, | ||
| 1553 | .fld32 => 0x0, | ||
| 1554 | .fld64 => 0x0, | ||
| 1495 | else => null, | 1555 | else => null, |
| 1496 | }; | 1556 | }; |
| 1497 | } | 1557 | } |
src/arch/x86_64/Mir.zig+14| ... | @@ -256,6 +256,20 @@ pub const Inst = struct { | ... | @@ -256,6 +256,20 @@ pub const Inst = struct { |
| 256 | /// TODO handle scaling | 256 | /// TODO handle scaling |
| 257 | movabs, | 257 | movabs, |
| 258 | 258 | ||
| 259 | /// ops flags: form: | ||
| 260 | /// 0b00 word ptr [reg1 + imm32] | ||
| 261 | /// 0b01 dword ptr [reg1 + imm32] | ||
| 262 | /// 0b10 qword ptr [reg1 + imm32] | ||
| 263 | /// Notes: | ||
| 264 | /// * source is always ST(0) | ||
| 265 | /// * only supports memory operands as destination | ||
| 266 | fisttp, | ||
| 267 | |||
| 268 | /// ops flags: form: | ||
| 269 | /// 0b01 dword ptr [reg1 + imm32] | ||
| 270 | /// 0b10 qword ptr [reg1 + imm32] | ||
| 271 | fld, | ||
| 272 | |||
| 259 | /// ops flags: form: | 273 | /// ops flags: form: |
| 260 | /// 0b00 inst | 274 | /// 0b00 inst |
| 261 | /// 0b01 reg1 | 275 | /// 0b01 reg1 |
test/behavior/cast.zig-1| ... | @@ -118,7 +118,6 @@ test "@intToFloat" { | ... | @@ -118,7 +118,6 @@ test "@intToFloat" { |
| 118 | 118 | ||
| 119 | test "@floatToInt" { | 119 | test "@floatToInt" { |
| 120 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 120 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 121 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 122 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 121 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 123 | 122 | ||
| 124 | try testFloatToInts(); | 123 | try testFloatToInts(); |