From 972d923f09f90ac15a2f2b13863598b268022d88 Mon Sep 17 00:00:00 2001 From: mparadinha Date: Mon, 14 Mar 2022 15:00:45 +0000 Subject: [PATCH 1/3] stage2: x86_64: add new `fisttp` instruction this instruction does truncating conversion from floating point values to signed integers. --- src/arch/x86_64/Emit.zig | 33 +++++++++++++++++++++++++++++++++ src/arch/x86_64/Mir.zig | 9 +++++++++ 2 files changed, 42 insertions(+) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index b6a3ffdf30b18d634afcefca68b8b8a2e0e54883..645fbf618c82ddf10ef34fd5e263564f54969f6a 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -131,6 +131,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .movabs => try emit.mirMovabs(inst), + .fisttp => try emit.mirFisttp(inst), + .lea => try emit.mirLea(inst), .lea_pie => try emit.mirLeaPie(inst), @@ -686,6 +688,28 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); } +fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .fisttp); + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + + // the selecting between operand sizes for this particular `fisttp` instruction + // is done via opcode instead of the usual prefixes. + + const opcode: Tag = switch (ops.flags) { + 0b00 => .fisttp16, + 0b01 => .fisttp32, + 0b10 => .fisttp64, + else => unreachable, + }; + const mem_or_reg = Memory{ + .base = ops.reg1, + .disp = emit.mir.instructions.items(.data)[inst].imm, + .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used + }; + return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); +} + fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { @@ -1114,6 +1138,9 @@ const Tag = enum { syscall, ret_near, ret_far, + fisttp16, + fisttp32, + fisttp64, jo, jno, jb, @@ -1352,6 +1379,9 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .setle, .setng => OpCode.twoByte(0x0f, 0x9e), .setnle, .setg => OpCode.twoByte(0x0f, 0x9f), .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), + .fisttp16 => OpCode.oneByte(0xdf), + .fisttp32 => OpCode.oneByte(0xdb), + .fisttp64 => OpCode.oneByte(0xdd), else => null, }, .o => return switch (tag) { @@ -1492,6 +1522,9 @@ inline fn getModRmExt(tag: Tag) ?u3 { .imul => 0x5, .idiv => 0x7, .div => 0x6, + .fisttp16 => 0x1, + .fisttp32 => 0x1, + .fisttp64 => 0x1, else => null, }; } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 9bb568a976ee8782387ff4838fffa80573cc303c..5ec028261aa3fcec67b9a707aecece3fda4300e1 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -256,6 +256,15 @@ pub const Inst = struct { /// TODO handle scaling movabs, + /// ops flags: form: + /// 0b00 word ptr [reg1 + imm32] + /// 0b01 dword ptr [reg1 + imm32] + /// 0b10 qword ptr [reg1 + imm32] + /// Notes: + /// * source is always ST(0) + /// * only supports memory operands as destination + fisttp, + /// ops flags: form: /// 0b00 inst /// 0b01 reg1 -- 2.54.0 From 66ef630006523c673e46a1e8d2496a99881e2452 Mon Sep 17 00:00:00 2001 From: mparadinha Date: Wed, 16 Mar 2022 19:15:41 +0000 Subject: [PATCH 2/3] stage2: x86_64: add new `fld` instruction --- src/arch/x86_64/Emit.zig | 27 +++++++++++++++++++++++++++ src/arch/x86_64/Mir.zig | 5 +++++ 2 files changed, 32 insertions(+) diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 645fbf618c82ddf10ef34fd5e263564f54969f6a..0543496750793dd12faae9a958aa095cbe3b044f 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -132,6 +132,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .movabs => try emit.mirMovabs(inst), .fisttp => try emit.mirFisttp(inst), + .fld => try emit.mirFld(inst), .lea => try emit.mirLea(inst), .lea_pie => try emit.mirLeaPie(inst), @@ -710,6 +711,26 @@ fn mirFisttp(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); } +fn mirFld(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { + const tag = emit.mir.instructions.items(.tag)[inst]; + assert(tag == .fld); + const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); + + // the selecting between operand sizes for this particular `fisttp` instruction + // is done via opcode instead of the usual prefixes. + + const opcode: Tag = switch (ops.flags) { + 0b01 => .fld32, + 0b10 => .fld64, + else => unreachable, + }; + const mem_or_reg = Memory{ + .base = ops.reg1, + .disp = emit.mir.instructions.items(.data)[inst].imm, + .ptr_size = Memory.PtrSize.dword_ptr, // to prevent any prefix from being used + }; + return lowerToMEnc(opcode, .{ .memory = mem_or_reg }, emit.code); +} fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); switch (ops.flags) { @@ -1141,6 +1162,8 @@ const Tag = enum { fisttp16, fisttp32, fisttp64, + fld32, + fld64, jo, jno, jb, @@ -1382,6 +1405,8 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { .fisttp16 => OpCode.oneByte(0xdf), .fisttp32 => OpCode.oneByte(0xdb), .fisttp64 => OpCode.oneByte(0xdd), + .fld32 => OpCode.oneByte(0xd9), + .fld64 => OpCode.oneByte(0xdd), else => null, }, .o => return switch (tag) { @@ -1525,6 +1550,8 @@ inline fn getModRmExt(tag: Tag) ?u3 { .fisttp16 => 0x1, .fisttp32 => 0x1, .fisttp64 => 0x1, + .fld32 => 0x0, + .fld64 => 0x0, else => null, }; } diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 5ec028261aa3fcec67b9a707aecece3fda4300e1..f4d6f454aee52dfca1ad994082d554aab9c76479 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -265,6 +265,11 @@ pub const Inst = struct { /// * only supports memory operands as destination fisttp, + /// ops flags: form: + /// 0b01 dword ptr [reg1 + imm32] + /// 0b10 qword ptr [reg1 + imm32] + fld, + /// ops flags: form: /// 0b00 inst /// 0b01 reg1 -- 2.54.0 From 355d0d0e7e061c86fbc08247f49900488e98acd1 Mon Sep 17 00:00:00 2001 From: mparadinha Date: Wed, 16 Mar 2022 21:19:08 +0000 Subject: [PATCH 3/3] stage2: x86_64: floatToInt for f32 and f64 --- src/arch/x86_64/CodeGen.zig | 55 +++++++++++++++++++++++++++++++++---- test/behavior/cast.zig | 1 - 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 63c85a5a9e6f0f16c71f487c15cf8e9b6628893e..a1bf128cce10bf2e3c99c7b682d4949bc90279dc 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -5437,11 +5437,56 @@ fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) - .dead - else - return self.fail("TODO implement airFloatToInt for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + if (self.liveness.isUnused(inst)) + return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); + + const src_ty = self.air.typeOf(ty_op.operand); + const dst_ty = self.air.typeOfIndex(inst); + const operand = try self.resolveInst(ty_op.operand); + + // move float src to ST(0) + const stack_offset = switch (operand) { + .stack_offset, .ptr_stack_offset => |offset| offset, + else => blk: { + const offset = @intCast(i32, try self.allocMem( + inst, + @intCast(u32, src_ty.abiSize(self.target.*)), + src_ty.abiAlignment(self.target.*), + )); + try self.genSetStack(src_ty, offset, operand, .{}); + break :blk offset; + }, + }; + _ = try self.addInst(.{ + .tag = .fld, + .ops = (Mir.Ops{ + .flags = switch (src_ty.abiSize(self.target.*)) { + 4 => 0b01, + 8 => 0b10, + else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}), + }, + .reg1 = .rbp, + }).encode(), + .data = .{ .imm = @bitCast(u32, -stack_offset) }, + }); + + // convert + const stack_dst = try self.allocRegOrMem(inst, false); + _ = try self.addInst(.{ + .tag = .fisttp, + .ops = (Mir.Ops{ + .flags = switch (dst_ty.abiSize(self.target.*)) { + 1...2 => 0b00, + 3...4 => 0b01, + 5...8 => 0b10, + else => |size| return self.fail("TODO convert float with abiSize={}", .{size}), + }, + .reg1 = .rbp, + }).encode(), + .data = .{ .imm = @bitCast(u32, -stack_dst.stack_offset) }, + }); + + return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none }); } fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 764e3b77b478328adee8773ba2f58ebac0a25e22..dd436ee2e932227a9384a4a7c9ee57083c53c4c0 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -118,7 +118,6 @@ test "@intToFloat" { test "@floatToInt" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try testFloatToInts(); -- 2.54.0