Merge pull request #3547 from pmatos/wip_x87_stack

x87 Stack Optimization
This commit is contained in:
Tony Wasserka authored and GitHub committed 2024-07-22 14:42:02 +02:00
commit d507f4c9b1
29 files changed
+71972 -119891

No files matched your search

+1
View File
@@ -143,6 +143,7 @@ set (SRCS
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
Interface/IR/Passes/DeadStoreElimination.cpp
Interface/IR/Passes/RegisterAllocationPass.cpp
Interface/IR/Passes/x87StackOptimizationPass.cpp
Utils/Telemetry.cpp
Utils/Threads.cpp
Utils/Profiler.cpp
@@ -10,6 +10,7 @@ $end_info$
#include "Interface/Context/Context.h"
#include "Interface/Core/OpcodeDispatcher.h"
#include "Interface/Core/X86Tables/X86Tables.h"
#include "Interface/IR/IR.h"
#include "Interface/IR/IREmitter.h"
#include <FEXCore/Config/Config.h>
@@ -6236,9 +6237,9 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
// 1 = Invalid
{OPDReg(0xD9, 2) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64WithWidth, 32>},
{OPDReg(0xD9, 2) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64, 32>},
{OPDReg(0xD9, 3) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64WithWidth, 32>},
{OPDReg(0xD9, 3) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64, 32>},
{OPDReg(0xD9, 4) | 0x00, 8, &OpDispatchBuilder::X87LDENVF64},
@@ -6248,16 +6249,16 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPDReg(0xD9, 7) | 0x00, 8, &OpDispatchBuilder::X87FSTCW},
{OPD(0xD9, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLDF64, 80>},
{OPD(0xD9, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLDFromStack>},
{OPD(0xD9, 0xC8), 8, &OpDispatchBuilder::FXCH},
{OPD(0xD9, 0xD0), 1, &OpDispatchBuilder::NOPOp}, // FNOP
// D1 = Invalid
// D8 = Invalid
{OPD(0xD9, 0xE0), 1, &OpDispatchBuilder::FCHSF64},
{OPD(0xD9, 0xE1), 1, &OpDispatchBuilder::FABSF64},
{OPD(0xD9, 0xE0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80STACKCHANGESIGN, false>},
{OPD(0xD9, 0xE1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80STACKABS, false>},
// E2 = Invalid
{OPD(0xD9, 0xE4), 1, &OpDispatchBuilder::FTSTF64},
{OPD(0xD9, 0xE5), 1, &OpDispatchBuilder::X87FXAMF64},
{OPD(0xD9, 0xE5), 1, &OpDispatchBuilder::X87FXAM},
// E6 = Invalid
{OPD(0xD9, 0xE8), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLDF64_Const, 0x3FF0000000000000>}, // 1.0
{OPD(0xD9, 0xE9), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLDF64_Const, 0x400A934F0979A372>}, // log2l(10)
@@ -6268,22 +6269,22 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(0xD9, 0xEE), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLDF64_Const, 0>}, // 0.0
// EF = Invalid
{OPD(0xD9, 0xF0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87UnaryOpF64, IR::OP_F64F2XM1>},
{OPD(0xD9, 0xF1), 1, &OpDispatchBuilder::X87FYL2XF64},
{OPD(0xD9, 0xF2), 1, &OpDispatchBuilder::X87TANF64},
{OPD(0xD9, 0xF3), 1, &OpDispatchBuilder::X87ATANF64},
{OPD(0xD9, 0xF4), 1, &OpDispatchBuilder::FXTRACTF64},
{OPD(0xD9, 0xF5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87BinaryOpF64, IR::OP_F64FPREM1>},
{OPD(0xD9, 0xF0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80F2XM1STACK, false>},
{OPD(0xD9, 0xF1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87FYL2X, false>},
{OPD(0xD9, 0xF2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80PTANSTACK, true>},
{OPD(0xD9, 0xF3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80ATANSTACK, false>},
{OPD(0xD9, 0xF4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80XTRACTSTACK, false>},
{OPD(0xD9, 0xF5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80FPREM1STACK, true>},
{OPD(0xD9, 0xF6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87ModifySTP, false>},
{OPD(0xD9, 0xF7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87ModifySTP, true>},
{OPD(0xD9, 0xF8), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87BinaryOpF64, IR::OP_F64FPREM>},
{OPD(0xD9, 0xF9), 1, &OpDispatchBuilder::X87FYL2XF64},
{OPD(0xD9, 0xFA), 1, &OpDispatchBuilder::FSQRTF64},
{OPD(0xD9, 0xFB), 1, &OpDispatchBuilder::X87SinCosF64},
{OPD(0xD9, 0xFC), 1, &OpDispatchBuilder::FRNDINTF64},
{OPD(0xD9, 0xFD), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87BinaryOpF64, IR::OP_F64SCALE>},
{OPD(0xD9, 0xFE), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87UnaryOpF64, IR::OP_F64SIN>},
{OPD(0xD9, 0xFF), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87UnaryOpF64, IR::OP_F64COS>},
{OPD(0xD9, 0xF8), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80FPREMSTACK, true>},
{OPD(0xD9, 0xF9), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87FYL2X, true>},
{OPD(0xD9, 0xFA), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80SQRTSTACK, false>},
{OPD(0xD9, 0xFB), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80SINCOSSTACK, true>},
{OPD(0xD9, 0xFC), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80ROUNDSTACK, false>},
{OPD(0xD9, 0xFD), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80SCALESTACK, false>},
{OPD(0xD9, 0xFE), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80SINSTACK, true>},
{OPD(0xD9, 0xFF), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80COSSTACK, true>},
{OPDReg(0xDA, 0) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FADDF64, 32, true, OpDispatchBuilder::OpResult::RES_ST0>},
@@ -6326,7 +6327,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
// 6 = Invalid
{OPDReg(0xDB, 7) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64WithWidth, 80>},
{OPDReg(0xDB, 7) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64, 80>},
{OPD(0xDB, 0xC0), 8, &OpDispatchBuilder::X87FCMOV},
@@ -6360,18 +6361,18 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(0xDC, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FADDF64, 80, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xC8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FMULF64, 80, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUBF64, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUBF64, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xF0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIVF64, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xF8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIVF64, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUBF64, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUBF64, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xF0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIVF64, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xF8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIVF64, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPDReg(0xDD, 0) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLDF64, 64>},
{OPDReg(0xDD, 1) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FISTF64, true>},
{OPDReg(0xDD, 2) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64WithWidth, 64>},
{OPDReg(0xDD, 2) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64, 64>},
{OPDReg(0xDD, 3) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64WithWidth, 64>},
{OPDReg(0xDD, 3) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTF64, 64>},
{OPDReg(0xDD, 4) | 0x00, 8, &OpDispatchBuilder::X87FRSTORF64},
@@ -6381,8 +6382,8 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPDReg(0xDD, 7) | 0x00, 8, &OpDispatchBuilder::X87FNSTSW},
{OPD(0xDD, 0xC0), 8, &OpDispatchBuilder::X87FFREE},
{OPD(0xDD, 0xD0), 8, &OpDispatchBuilder::FST}, // register-register from regular X87
{OPD(0xDD, 0xD8), 8, &OpDispatchBuilder::FST}, //^
{OPD(0xDD, 0xD0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTToStack>}, // register-register from regular X87
{OPD(0xDD, 0xD8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTToStack>}, //^
{OPD(0xDD, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FCOMIF64, 80, false, OpDispatchBuilder::FCOMIFlags::FLAGS_X87, false>},
{OPD(0xDD, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FCOMIF64, 80, false, OpDispatchBuilder::FCOMIFlags::FLAGS_X87, false>},
@@ -6406,10 +6407,10 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(0xDE, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FADDF64, 80, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xC8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FMULF64, 80, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xD9), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FCOMIF64, 80, false, OpDispatchBuilder::FCOMIFlags::FLAGS_X87, true>},
{OPD(0xDE, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUBF64, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUBF64, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xF0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIVF64, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xF8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIVF64, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUBF64, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUBF64, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xF0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIVF64, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xF8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIVF64, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPDReg(0xDF, 0) | 0x00, 8, &OpDispatchBuilder::FILDF64},
@@ -6466,9 +6467,9 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
// 1 = Invalid
{OPDReg(0xD9, 2) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTWithWidth, 32>},
{OPDReg(0xD9, 2) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FST, 32>},
{OPDReg(0xD9, 3) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTWithWidth, 32>},
{OPDReg(0xD9, 3) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FST, 32>},
{OPDReg(0xD9, 4) | 0x00, 8, &OpDispatchBuilder::X87LDENV},
@@ -6478,13 +6479,13 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPDReg(0xD9, 7) | 0x00, 8, &OpDispatchBuilder::X87FSTCW},
{OPD(0xD9, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLD, 80>},
{OPD(0xD9, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLDFromStack>},
{OPD(0xD9, 0xC8), 8, &OpDispatchBuilder::FXCH},
{OPD(0xD9, 0xD0), 1, &OpDispatchBuilder::NOPOp}, // FNOP
// D1 = Invalid
// D8 = Invalid
{OPD(0xD9, 0xE0), 1, &OpDispatchBuilder::FCHS},
{OPD(0xD9, 0xE1), 1, &OpDispatchBuilder::FABS},
{OPD(0xD9, 0xE0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80STACKCHANGESIGN, false>},
{OPD(0xD9, 0xE1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80STACKABS, false>},
// E2 = Invalid
{OPD(0xD9, 0xE4), 1, &OpDispatchBuilder::FTST},
{OPD(0xD9, 0xE5), 1, &OpDispatchBuilder::X87FXAM},
@@ -6498,22 +6499,22 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(0xD9, 0xEE), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLD_Const, NamedVectorConstant::NAMED_VECTOR_ZERO>}, // 0.0
// EF = Invalid
{OPD(0xD9, 0xF0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87UnaryOp, IR::OP_F80F2XM1>},
{OPD(0xD9, 0xF1), 1, &OpDispatchBuilder::X87FYL2X},
{OPD(0xD9, 0xF2), 1, &OpDispatchBuilder::X87TAN},
{OPD(0xD9, 0xF3), 1, &OpDispatchBuilder::X87ATAN},
{OPD(0xD9, 0xF4), 1, &OpDispatchBuilder::FXTRACT},
{OPD(0xD9, 0xF5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87BinaryOp, IR::OP_F80FPREM1>},
{OPD(0xD9, 0xF0), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80F2XM1STACK, false>},
{OPD(0xD9, 0xF1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87FYL2X, false>},
{OPD(0xD9, 0xF2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80PTANSTACK, true>},
{OPD(0xD9, 0xF3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80ATANSTACK, false>},
{OPD(0xD9, 0xF4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80XTRACTSTACK, false>},
{OPD(0xD9, 0xF5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80FPREM1STACK, true>},
{OPD(0xD9, 0xF6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87ModifySTP, false>},
{OPD(0xD9, 0xF7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87ModifySTP, true>},
{OPD(0xD9, 0xF8), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87BinaryOp, IR::OP_F80FPREM>},
{OPD(0xD9, 0xF9), 1, &OpDispatchBuilder::X87FYL2X},
{OPD(0xD9, 0xFA), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87UnaryOp, IR::OP_F80SQRT>},
{OPD(0xD9, 0xFB), 1, &OpDispatchBuilder::X87SinCos},
{OPD(0xD9, 0xFC), 1, &OpDispatchBuilder::FRNDINT},
{OPD(0xD9, 0xFD), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87BinaryOp, IR::OP_F80SCALE>},
{OPD(0xD9, 0xFE), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87UnaryOp, IR::OP_F80SIN>},
{OPD(0xD9, 0xFF), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87UnaryOp, IR::OP_F80COS>},
{OPD(0xD9, 0xF8), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80FPREMSTACK, true>},
{OPD(0xD9, 0xF9), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87FYL2X, true>},
{OPD(0xD9, 0xFA), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80SQRTSTACK, false>},
{OPD(0xD9, 0xFB), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80SINCOSSTACK, true>},
{OPD(0xD9, 0xFC), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80ROUNDSTACK, false>},
{OPD(0xD9, 0xFD), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80SCALESTACK, false>},
{OPD(0xD9, 0xFE), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80SINSTACK, true>},
{OPD(0xD9, 0xFF), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80COSSTACK, true>},
{OPDReg(0xDA, 0) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FADD, 32, true, OpDispatchBuilder::OpResult::RES_ST0>},
@@ -6556,7 +6557,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
// 6 = Invalid
{OPDReg(0xDB, 7) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTWithWidth, 80>},
{OPDReg(0xDB, 7) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FST, 80>},
{OPD(0xDB, 0xC0), 8, &OpDispatchBuilder::X87FCMOV},
@@ -6590,18 +6591,18 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(0xDC, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FADD, 80, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xC8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FMUL, 80, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUB, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUB, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xF0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIV, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xF8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIV, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUB, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUB, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xF0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIV, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDC, 0xF8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIV, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPDReg(0xDD, 0) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FLD, 64>},
{OPDReg(0xDD, 1) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FIST, true>},
{OPDReg(0xDD, 2) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTWithWidth, 64>},
{OPDReg(0xDD, 2) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FST, 64>},
{OPDReg(0xDD, 3) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTWithWidth, 64>},
{OPDReg(0xDD, 3) | 0x00, 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FST, 64>},
{OPDReg(0xDD, 4) | 0x00, 8, &OpDispatchBuilder::X87FRSTOR},
@@ -6611,8 +6612,8 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPDReg(0xDD, 7) | 0x00, 8, &OpDispatchBuilder::X87FNSTSW},
{OPD(0xDD, 0xC0), 8, &OpDispatchBuilder::X87FFREE},
{OPD(0xDD, 0xD0), 8, &OpDispatchBuilder::FST},
{OPD(0xDD, 0xD8), 8, &OpDispatchBuilder::FST},
{OPD(0xDD, 0xD0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTToStack>},
{OPD(0xDD, 0xD8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSTToStack>},
{OPD(0xDD, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FCOMI, 80, false, OpDispatchBuilder::FCOMIFlags::FLAGS_X87, false>},
{OPD(0xDD, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FCOMI, 80, false, OpDispatchBuilder::FCOMIFlags::FLAGS_X87, false>},
@@ -6636,10 +6637,10 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(0xDE, 0xC0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FADD, 80, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xC8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FMUL, 80, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xD9), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FCOMI, 80, false, OpDispatchBuilder::FCOMIFlags::FLAGS_X87, true>},
{OPD(0xDE, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUB, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUB, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xF0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIV, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xF8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIV, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xE0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUB, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xE8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FSUB, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xF0), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIV, 80, false, true, OpDispatchBuilder::OpResult::RES_STI>},
{OPD(0xDE, 0xF8), 8, &OpDispatchBuilder::Bind<&OpDispatchBuilder::FDIV, 80, false, false, OpDispatchBuilder::OpResult::RES_STI>},
{OPDReg(0xDF, 0) | 0x00, 8, &OpDispatchBuilder::FILD},
@@ -731,45 +731,42 @@ public:
void VZEROOp(OpcodeArgs);
// X87 Ops
Ref ReconstructFSW();
Ref ReconstructFSW_Helper(Ref T = nullptr);
// Returns new x87 stack top from FSW.
Ref ReconstructX87StateFromFSW(Ref FSW);
void FLD(OpcodeArgs, size_t width);
void FLD_Const(OpcodeArgs, NamedVectorConstant constant);
Ref ReconstructX87StateFromFSW_Helper(Ref FSW);
void FLD(OpcodeArgs, size_t Width);
void FLDFromStack(OpcodeArgs);
void FLD_Const(OpcodeArgs, NamedVectorConstant Constant);
void FBLD(OpcodeArgs);
void FBSTP(OpcodeArgs);
void FILD(OpcodeArgs);
void FSTWithWidth(OpcodeArgs, size_t width);
void FST(OpcodeArgs);
void FST(OpcodeArgs, size_t Width);
void FSTToStack(OpcodeArgs);
void FIST(OpcodeArgs, bool Truncate);
// OpResult is used for Stack operations,
// describes if the result of the operation is stored in ST(0) or ST(i),
// where ST(i) is one of the arguments to the operation.
enum class OpResult {
RES_ST0,
RES_STI,
};
void FADD(OpcodeArgs, size_t width, bool Integer, OpResult ResInST0);
void FMUL(OpcodeArgs, size_t width, bool Integer, OpResult ResInST0);
void FDIV(OpcodeArgs, size_t width, bool Integer, bool reverse, OpResult ResInST0);
void FSUB(OpcodeArgs, size_t width, bool Integer, bool reverse, OpResult ResInST0);
void FCHS(OpcodeArgs);
void FABS(OpcodeArgs);
void X87OpHelper(OpcodeArgs, FEXCore::IR::IROps IROp, bool ZeroC2);
void FADD(OpcodeArgs, size_t Width, bool Integer, OpResult ResInST0);
void FMUL(OpcodeArgs, size_t Width, bool Integer, OpResult ResInST0);
void FDIV(OpcodeArgs, size_t Width, bool Integer, bool Reverse, OpResult ResInST0);
void FSUB(OpcodeArgs, size_t Width, bool Integer, bool Reverse, OpResult ResInST0);
void FTST(OpcodeArgs);
void FRNDINT(OpcodeArgs);
void FXTRACT(OpcodeArgs);
void FNINIT(OpcodeArgs);
void X87UnaryOp(OpcodeArgs, FEXCore::IR::IROps IROp);
void X87BinaryOp(OpcodeArgs, FEXCore::IR::IROps IROp);
void X87ModifySTP(OpcodeArgs, bool Inc);
void X87SinCos(OpcodeArgs);
void X87FYL2X(OpcodeArgs);
void X87TAN(OpcodeArgs);
void X87ATAN(OpcodeArgs);
void X87FYL2X(OpcodeArgs, bool IsFYL2XP1);
void X87LDENV(OpcodeArgs);
void X87FLDCW(OpcodeArgs);
void X87FNSTENV(OpcodeArgs);
@@ -789,27 +786,25 @@ public:
FLAGS_X87,
FLAGS_RFLAGS,
};
void FCOMI(OpcodeArgs, size_t width, bool Integer, FCOMIFlags whichflags, bool poptwice);
void FCOMI(OpcodeArgs, size_t Width, bool Integer, FCOMIFlags WhichFlags, bool PopTwice);
// F64 X87 Ops
void FLDF64(OpcodeArgs, size_t width);
void FLDF64_Const(OpcodeArgs, uint64_t num);
void FLDF64(OpcodeArgs, size_t Width);
void FLDF64_Const(OpcodeArgs, uint64_t Num);
void FBLDF64(OpcodeArgs);
void FBSTPF64(OpcodeArgs);
void FILDF64(OpcodeArgs);
void FSTF64WithWidth(OpcodeArgs, size_t width);
void FSTF64(OpcodeArgs);
void FSTF64(OpcodeArgs, size_t Width);
void FISTF64(OpcodeArgs, bool Truncate);
void FADDF64(OpcodeArgs, size_t width, bool Integer, OpResult ResInST0);
void FMULF64(OpcodeArgs, size_t width, bool Integer, OpResult ResInST0);
void FDIVF64(OpcodeArgs, size_t width, bool Integer, bool reverse, OpResult ResInST0);
void FSUBF64(OpcodeArgs, size_t width, bool Integer, bool reverse, OpResult ResInST0);
void FADDF64(OpcodeArgs, size_t Width, bool Integer, OpResult ResInST0);
void FMULF64(OpcodeArgs, size_t Width, bool Integer, OpResult ResInST0);
void FDIVF64(OpcodeArgs, size_t Width, bool Integer, bool Reverse, OpResult ResInST0);
void FSUBF64(OpcodeArgs, size_t Width, bool Integer, bool Reverse, OpResult ResInST0);
void FCHSF64(OpcodeArgs);
void FABSF64(OpcodeArgs);
void FTSTF64(OpcodeArgs);
@@ -821,7 +816,6 @@ public:
void X87BinaryOpF64(OpcodeArgs, FEXCore::IR::IROps IROp);
void X87SinCosF64(OpcodeArgs);
void X87FLDCWF64(OpcodeArgs);
void X87FYL2XF64(OpcodeArgs);
void X87TANF64(OpcodeArgs);
void X87ATANF64(OpcodeArgs);
void X87FNSAVEF64(OpcodeArgs);
@@ -1323,6 +1317,8 @@ protected:
}
private:
FEX_CONFIG_OPT(ReducedPrecisionMode, X87REDUCEDPRECISION);
struct JumpTargetInfo {
Ref BlockEntry;
bool HaveEmitted;
@@ -1962,7 +1958,11 @@ private:
// First, set flags according to Arm FCMP.
HandleNZCVWrite();
_FCmp(ElementSize, Src1, Src2);
ComissFlags(InvalidateAF);
}
// Sets flags for a COMISS instruction
void ComissFlags(bool InvalidateAF = false) {
// Now set COMISS flags by converts NZCV from the Arm representation to an
// eXternal representation that's totally not a euphemism for x86, nuh-uh.
if (CTX->HostFeatures.SupportsFlagM2) {
@@ -2262,12 +2262,9 @@ private:
/** @} */
Ref GetX87Top();
void SetX87ValidTag(Ref Value, bool Valid);
Ref GetX87ValidTag(Ref Value);
Ref GetX87Tag(Ref Value, Ref AbridgedFTW);
Ref GetX87Tag(Ref Value);
void SetX87FTW(Ref FTW);
Ref GetX87FTW();
Ref GetX87FTW_Helper();
void SetX87Top(Ref Value);
bool DestIsLockedMem(FEXCore::X86Tables::DecodedOp Op) const {
@@ -2688,7 +2688,7 @@ void OpDispatchBuilder::SaveX87State(OpcodeArgs, Ref MemBase) {
_StoreMem(GPRClass, 2, MemBase, FCW, 2);
}
{ _StoreMem(GPRClass, 2, ReconstructFSW(), MemBase, _Constant(2), 2, MEM_OFFSET_SXTX, 1); }
{ _StoreMem(GPRClass, 2, ReconstructFSW_Helper(), MemBase, _Constant(2), 2, MEM_OFFSET_SXTX, 1); }
{
// Abridged FTW
@@ -2863,7 +2863,7 @@ void OpDispatchBuilder::RestoreX87State(Ref MemBase) {
{
auto NewFSW = _LoadMem(GPRClass, 2, MemBase, _Constant(2), 2, MEM_OFFSET_SXTX, 1);
ReconstructX87StateFromFSW(NewFSW);
ReconstructX87StateFromFSW_Helper(NewFSW);
}
{
File diff suppressed because it is too large. Load diff
@@ -40,10 +40,13 @@ class OrderedNode;
// FXCH
// FCMOV
// FST(register to register)
// FCHS
// State loading duplicated from X87.cpp, setting host rounding mode
// See issue
void OpDispatchBuilder::FNINITF64(OpcodeArgs) {
// FIXME: almost a duplicate of x87 version.
// Init FCW to 0x037F
auto NewFCW = _Constant(16, 0x037F);
// Init host rounding mode to zero
@@ -53,17 +56,20 @@ void OpDispatchBuilder::FNINITF64(OpcodeArgs) {
// Init FSW to 0
SetX87Top(Zero);
// Tags all get marked as invalid
StoreContext(AbridgedFTWIndex, Zero);
_InitStack();
SetRFLAG<FEXCore::X86State::X87FLAG_C0_LOC>(Zero);
SetRFLAG<FEXCore::X86State::X87FLAG_C1_LOC>(Zero);
SetRFLAG<FEXCore::X86State::X87FLAG_C2_LOC>(Zero);
SetRFLAG<FEXCore::X86State::X87FLAG_C3_LOC>(Zero);
// Tags all get marked as invalid
StoreContext(AbridgedFTWIndex, Zero);
}
void OpDispatchBuilder::X87LDENVF64(OpcodeArgs) {
_StackForceSlow();
const auto Size = GetSrcSize(Op);
Ref Mem = MakeSegmentAddress(Op, Op->Src[0]);
@@ -75,7 +81,7 @@ void OpDispatchBuilder::X87LDENVF64(OpcodeArgs) {
_StoreContext(2, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
auto NewFSW = _LoadMem(GPRClass, Size, Mem, _Constant(Size * 1), Size, MEM_OFFSET_SXTX, 1);
ReconstructX87StateFromFSW(NewFSW);
ReconstructX87StateFromFSW_Helper(NewFSW);
{
// FTW
@@ -85,6 +91,8 @@ void OpDispatchBuilder::X87LDENVF64(OpcodeArgs) {
void OpDispatchBuilder::X87FLDCWF64(OpcodeArgs) {
_StackForceSlow();
Ref NewFCW = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
// ignore the rounding precision, we're always 64-bit in F64.
// extract rounding mode
@@ -94,132 +102,65 @@ void OpDispatchBuilder::X87FLDCWF64(OpcodeArgs) {
}
// F64 ops
void OpDispatchBuilder::FLDF64(OpcodeArgs, size_t width) {
// Update TOP
auto orig_top = GetX87Top();
auto mask = _Constant(7);
size_t read_width = (width == 80) ? 16 : width / 8;
Ref data {};
Ref converted {};
if (!Op->Src[0].IsNone()) {
// Read from memory
data = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], read_width, Op->Flags);
// Convert to 64bit float
if (width == 32) {
converted = _Float_FToF(8, 4, data);
} else if (width == 80) {
converted = _F80CVT(8, data);
} else {
converted = data;
}
} else {
// Implicit arg (does this need to change with width?)
auto offset = _Constant(Op->OP & 7);
data = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, orig_top, offset), mask);
data = _LoadContextIndexed(data, 8, MMBaseOffset(), 16, FPRClass);
converted = data;
// Float load op with memory operand
void OpDispatchBuilder::FLDF64(OpcodeArgs, size_t Width) {
size_t ReadWidth = (Width == 80) ? 16 : Width / 8;
Ref Data = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], ReadWidth, Op->Flags);
// Convert to 64bit float
Ref ConvertedData = Data;
if (Width == 32) {
ConvertedData = _Float_FToF(8, 4, Data);
} else if (Width == 80) {
ConvertedData = _F80CVT(8, Data);
}
auto top = _And(OpSize::i32Bit, _Sub(OpSize::i32Bit, orig_top, _Constant(1)), mask);
SetX87ValidTag(top, true);
SetX87Top(top);
// Write to ST[TOP]
_StoreContextIndexed(converted, top, 8, MMBaseOffset(), 16, FPRClass);
_PushStack(ConvertedData, Data, ReadWidth, true);
}
void OpDispatchBuilder::FBLDF64(OpcodeArgs) {
// Update TOP
auto orig_top = GetX87Top();
auto mask = _Constant(7);
auto top = _And(OpSize::i32Bit, _Sub(OpSize::i32Bit, orig_top, _Constant(1)), mask);
SetX87ValidTag(top, true);
SetX87Top(top);
// Read from memory
Ref data = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], 16, Op->Flags);
Ref converted = _F80BCDLoad(data);
converted = _F80CVT(8, converted);
_StoreContextIndexed(converted, top, 8, MMBaseOffset(), 16, FPRClass);
Ref Data = LoadSource_WithOpSize(FPRClass, Op, Op->Src[0], 16, Op->Flags);
Ref ConvertedData = _F80BCDLoad(Data);
ConvertedData = _F80CVT(8, ConvertedData);
_PushStack(ConvertedData, Data, 8, true);
}
void OpDispatchBuilder::FBSTPF64(OpcodeArgs) {
auto orig_top = GetX87Top();
auto data = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
Ref converted = _F80CVTTo(data, 8);
Ref converted = _F80CVTTo(_ReadStackValue(0), 8);
converted = _F80BCDStore(converted);
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, converted, 10, 1);
// if we are popping then we must first mark this location as empty
SetX87ValidTag(orig_top, false);
auto top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87Top(top);
_PopStackDestroy();
}
void OpDispatchBuilder::FLDF64_Const(OpcodeArgs, uint64_t num) {
// Update TOP
auto orig_top = GetX87Top();
auto top = _And(OpSize::i32Bit, _Sub(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87ValidTag(top, true);
SetX87Top(top);
auto data = _VCastFromGPR(8, 8, _Constant(num));
// Write to ST[TOP]
_StoreContextIndexed(data, top, 8, MMBaseOffset(), 16, FPRClass);
void OpDispatchBuilder::FLDF64_Const(OpcodeArgs, uint64_t Num) {
auto Data = _VCastFromGPR(8, 8, _Constant(Num));
_PushStack(Data, Data, 8, true);
}
void OpDispatchBuilder::FILDF64(OpcodeArgs) {
// Update TOP
auto orig_top = GetX87Top();
auto top = _And(OpSize::i32Bit, _Sub(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87ValidTag(top, true);
SetX87Top(top);
size_t ReadWidth = GetSrcSize(Op);
size_t read_width = GetSrcSize(Op);
// Read from memory
auto data = LoadSource_WithOpSize(GPRClass, Op, Op->Src[0], read_width, Op->Flags);
if (read_width == 2) {
data = _Sbfe(OpSize::i64Bit, read_width * 8, 0, data);
Ref Data = LoadSource_WithOpSize(GPRClass, Op, Op->Src[0], ReadWidth, Op->Flags);
if (ReadWidth == 2) {
Data = _Sbfe(OpSize::i64Bit, ReadWidth * 8, 0, Data);
}
auto converted = _Float_FromGPR_S(8, read_width == 4 ? 4 : 8, data);
// Write to ST[TOP]
_StoreContextIndexed(converted, top, 8, MMBaseOffset(), 16, FPRClass);
auto ConvertedData = _Float_FromGPR_S(8, ReadWidth == 4 ? 4 : 8, Data);
_PushStack(ConvertedData, Data, ReadWidth, false);
}
void OpDispatchBuilder::FSTF64WithWidth(OpcodeArgs, size_t width) {
auto orig_top = GetX87Top();
auto data = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
if (width == 64) {
// Store 64-bit float directly
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, data, 8, 1);
} else if (width == 32) {
// Convert to 32-bit float and store
auto result = _Float_FToF(4, 8, data);
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, result, 4, 1);
} else if (width == 80) {
// Convert to 80-bit float
auto result = _F80CVTTo(data, 8);
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, result, 10, 1);
}
void OpDispatchBuilder::FSTF64(OpcodeArgs, size_t Width) {
Ref Mem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.LoadData = false});
_StoreStackMemory(Mem, OpSize::i64Bit, true, Width / 8);
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
// if we are popping then we must first mark this location as empty
SetX87ValidTag(orig_top, false);
// Set the new top now
auto top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87Top(top);
if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) {
_PopStackDestroy();
}
}
void OpDispatchBuilder::FISTF64(OpcodeArgs, bool Truncate) {
auto Size = GetSrcSize(Op);
auto orig_top = GetX87Top();
Ref data = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
Ref data = _ReadStackValue(0);
if (Truncate) {
data = _Float_ToGPR_ZS(Size == 4 ? 4 : 8, 8, data);
} else {
@@ -228,468 +169,255 @@ void OpDispatchBuilder::FISTF64(OpcodeArgs, bool Truncate) {
StoreResult_WithOpSize(GPRClass, Op, Op->Dest, data, Size, 1);
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
// if we are popping then we must first mark this location as empty
SetX87ValidTag(orig_top, false);
// Set the new top now
auto top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87Top(top);
_PopStackDestroy();
}
}
void OpDispatchBuilder::FADDF64(OpcodeArgs, size_t width, bool Integer, OpDispatchBuilder::OpResult ResInST0) {
auto top = GetX87Top();
Ref StackLocation = top;
void OpDispatchBuilder::FADDF64(OpcodeArgs, size_t Width, bool Integer, OpDispatchBuilder::OpResult ResInST0) {
if (Op->Src[0].IsNone()) { // Implicit argument case
auto Offset = Op->OP & 7;
auto St0 = 0;
if (ResInST0 == OpResult::RES_STI) {
_F80AddStack(Offset, St0);
} else {
_F80AddStack(St0, Offset);
}
if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) {
_PopStackDestroy();
}
return;
}
// We have one memory argument
Ref arg {};
Ref b {};
auto mask = _Constant(7);
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
if (Width == 16) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
arg = _Float_FromGPR_S(8, Width == 64 ? 8 : 4, arg);
} else if (Width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = _Float_FToF(8, 4, arg);
} else if (Width == 64) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
}
if (!Op->Src[0].IsNone()) {
// Memory arg
// top of stack is at offset zero
_F80AddValue(0, arg);
}
// FIXME: following is very similar to FADDF64
void OpDispatchBuilder::FMULF64(OpcodeArgs, size_t Width, bool Integer, OpDispatchBuilder::OpResult ResInST0) {
if (Op->Src[0].IsNone()) { // Implicit argument case
auto offset = Op->OP & 7;
auto st0 = 0;
if (ResInST0 == OpResult::RES_STI) {
_F80MulStack(offset, st0);
} else {
_F80MulStack(st0, offset);
}
if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) {
_PopStackDestroy();
}
return;
}
// We have one memory argument
Ref arg {};
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
if (Width == 16) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
arg = _Float_FromGPR_S(8, Width == 64 ? 8 : 4, arg);
} else if (Width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
arg = _Float_FToF(8, 4, arg);
} else if (Width == 64) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
}
// top of stack is at offset zero
_F80MulValue(0, arg);
if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) {
_PopStackDestroy();
}
}
void OpDispatchBuilder::FDIVF64(OpcodeArgs, size_t Width, bool Integer, bool Reverse, OpDispatchBuilder::OpResult ResInST0) {
if (Op->Src[0].IsNone()) {
const auto offset = Op->OP & 7;
const auto st0 = 0;
if (Reverse) {
if (ResInST0 == OpResult::RES_STI) {
_F80DivStack(offset, st0, offset);
} else {
_F80DivStack(st0, offset, st0);
}
} else {
if (ResInST0 == OpResult::RES_STI) {
_F80DivStack(offset, offset, st0);
} else {
_F80DivStack(st0, st0, offset);
}
}
if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) {
_PopStackDestroy();
}
return;
}
// We have one memory argument
Ref Arg {};
if (Width == 16 || Width == 32 || Width == 64) {
if (Integer) {
Arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
if (Width == 16) {
Arg = _Sbfe(OpSize::i64Bit, 16, 0, Arg);
}
Arg = _Float_FromGPR_S(8, Width == 64 ? 8 : 4, Arg);
} else if (Width == 32) {
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
Arg = _Float_FToF(8, 4, Arg);
} else if (Width == 64) {
Arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
}
}
// top of stack is at offset zero
if (Reverse) {
_F80DivRValue(Arg, 0);
} else {
_F80DivValue(0, Arg);
}
if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) {
_PopStackDestroy();
}
}
void OpDispatchBuilder::FSUBF64(OpcodeArgs, size_t Width, bool Integer, bool Reverse, OpDispatchBuilder::OpResult ResInST0) {
if (Op->Src[0].IsNone()) {
const auto Offset = Op->OP & 7;
const auto St0 = 0;
if (Reverse) {
if (ResInST0 == OpResult::RES_STI) {
_F80SubStack(Offset, St0, Offset);
} else {
_F80SubStack(St0, Offset, St0);
}
} else {
if (ResInST0 == OpResult::RES_STI) {
_F80SubStack(Offset, Offset, St0);
} else {
_F80SubStack(St0, St0, Offset);
}
}
if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) {
_PopStackDestroy();
}
return;
}
// We have one memory argument
Ref arg {};
if (Width == 16 || Width == 32 || Width == 64) {
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
if (width == 16) {
if (Width == 16) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if (width == 32) {
arg = _Float_FromGPR_S(8, Width == 64 ? 8 : 4, arg);
} else if (Width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
b = _Float_FToF(8, 4, arg);
} else if (width == 64) {
b = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
}
} else {
// Implicit arg
auto offset = _Constant(Op->OP & 7);
arg = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, offset), mask);
if (ResInST0 == OpResult::RES_STI) {
StackLocation = arg;
}
b = _LoadContextIndexed(arg, 8, MMBaseOffset(), 16, FPRClass);
}
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
auto result = _VFAdd(8, 8, a, b);
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
// if we are popping then we must first mark this location as empty
SetX87ValidTag(top, false);
// Set the new top now
top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, _Constant(1)), mask);
SetX87Top(top);
}
// Write to ST[TOP]
_StoreContextIndexed(result, StackLocation, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::FMULF64(OpcodeArgs, size_t width, bool Integer, OpDispatchBuilder::OpResult ResInST0) {
auto top = GetX87Top();
Ref StackLocation = top;
Ref arg {};
Ref b {};
auto mask = _Constant(7);
if (!Op->Src[0].IsNone()) {
// Memory arg
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
if (width == 16) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if (width == 32) {
arg = _Float_FToF(8, 4, arg);
} else if (Width == 64) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
b = _Float_FToF(8, 4, arg);
} else if (width == 64) {
b = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
}
}
// top of stack is at offset zero
if (Reverse) {
_F80SubRValue(arg, 0);
} else {
// Implicit arg
auto offset = _Constant(Op->OP & 7);
arg = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, offset), mask);
if (ResInST0 == OpResult::RES_STI) {
StackLocation = arg;
}
b = _LoadContextIndexed(arg, 8, MMBaseOffset(), 16, FPRClass);
_F80SubValue(0, arg);
}
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
auto result = _VFMul(8, 8, a, b);
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
// if we are popping then we must first mark this location as empty
SetX87ValidTag(top, false);
// Set the new top now
top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, _Constant(1)), mask);
SetX87Top(top);
if (Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) {
_PopStackDestroy();
}
// Write to ST[TOP]
_StoreContextIndexed(result, StackLocation, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::FDIVF64(OpcodeArgs, size_t width, bool Integer, bool reverse, OpDispatchBuilder::OpResult ResInST0) {
auto top = GetX87Top();
Ref StackLocation = top;
Ref arg {};
Ref b {};
auto mask = _Constant(7);
if (!Op->Src[0].IsNone()) {
// Memory arg
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
if (width == 16) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if (width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
b = _Float_FToF(8, 4, arg);
} else if (width == 64) {
b = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
}
} else {
// Implicit arg
auto offset = _Constant(Op->OP & 7);
arg = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, offset), mask);
if (ResInST0 == OpResult::RES_STI) {
StackLocation = arg;
}
b = _LoadContextIndexed(arg, 8, MMBaseOffset(), 16, FPRClass);
}
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
Ref result {};
if (reverse) {
result = _VFDiv(8, 8, b, a);
} else {
result = _VFDiv(8, 8, a, b);
}
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
// if we are popping then we must first mark this location as empty
SetX87ValidTag(top, false);
// Set the new top now
top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, _Constant(1)), mask);
SetX87Top(top);
}
// Write to ST[TOP]
_StoreContextIndexed(result, StackLocation, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::FSUBF64(OpcodeArgs, size_t width, bool Integer, bool reverse, OpDispatchBuilder::OpResult ResInST0) {
auto top = GetX87Top();
Ref StackLocation = top;
Ref arg {};
Ref b {};
auto mask = _Constant(7);
if (!Op->Src[0].IsNone()) {
// Memory arg
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
if (width == 16) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if (width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
b = _Float_FToF(8, 4, arg);
} else if (width == 64) {
b = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
}
} else {
// Implicit arg
auto offset = _Constant(Op->OP & 7);
arg = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, offset), mask);
if (ResInST0 == OpResult::RES_STI) {
StackLocation = arg;
}
b = _LoadContextIndexed(arg, 8, MMBaseOffset(), 16, FPRClass);
}
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
Ref result {};
if (reverse) {
result = _VFSub(8, 8, b, a);
} else {
result = _VFSub(8, 8, a, b);
}
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
// if we are popping then we must first mark this location as empty
SetX87ValidTag(top, false);
// Set the new top now
top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, _Constant(1)), mask);
SetX87Top(top);
}
// Write to ST[TOP]
_StoreContextIndexed(result, StackLocation, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::FCHSF64(OpcodeArgs) {
auto top = GetX87Top();
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
auto result = _VFNeg(8, 8, a);
// Write to ST[TOP]
_StoreContextIndexed(result, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::FABSF64(OpcodeArgs) {
auto top = GetX87Top();
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
auto result = _VFAbs(8, 8, a);
// Write to ST[TOP]
_StoreContextIndexed(result, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::FTSTF64(OpcodeArgs) {
auto top = GetX87Top();
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
auto low = _Constant(0);
Ref data = _VCastFromGPR(8, 8, low);
// We are going to clobber NZCV, make sure it's in a GPR first.
GetNZCV();
// Now we do our comparison.
_FCmp(8, a, data);
_F80StackTest(0);
PossiblySetNZCVBits = ~0;
ConvertNZCVToX87();
}
// TODO: This should obey rounding mode
void OpDispatchBuilder::FRNDINTF64(OpcodeArgs) {
auto top = GetX87Top();
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
auto result = _Vector_FToI(8, 8, a, FEXCore::IR::Round_Host);
// Write to ST[TOP]
_StoreContextIndexed(result, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::FXTRACTF64(OpcodeArgs) {
auto orig_top = GetX87Top();
auto top = _And(OpSize::i32Bit, _Sub(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87ValidTag(top, true);
SetX87Top(top);
auto a = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
auto gpr = _VExtractToGPR(8, 8, a, 0);
Ref exp = _And(OpSize::i64Bit, gpr, _Constant(0x7ff0000000000000LL));
exp = _Lshr(OpSize::i64Bit, exp, _Constant(52));
exp = _Sub(OpSize::i64Bit, exp, _Constant(1023));
exp = _Float_FromGPR_S(8, 8, exp);
Ref sig = _And(OpSize::i64Bit, gpr, _Constant(0x800fffffffffffffLL));
sig = _Or(OpSize::i64Bit, sig, _Constant(0x3ff0000000000000LL));
sig = _VCastFromGPR(8, 8, sig);
// Write to ST[TOP]
_StoreContextIndexed(exp, orig_top, 8, MMBaseOffset(), 16, FPRClass);
_StoreContextIndexed(sig, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::FCOMIF64(OpcodeArgs, size_t width, bool Integer, OpDispatchBuilder::FCOMIFlags whichflags, bool poptwice) {
auto top = GetX87Top();
auto mask = _Constant(7);
void OpDispatchBuilder::FCOMIF64(OpcodeArgs, size_t Width, bool Integer, OpDispatchBuilder::FCOMIFlags WhichFlags, bool PopTwice) {
Ref arg {};
Ref b {};
if (!Op->Src[0].IsNone()) {
// Memory arg
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
if (width == 16) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
} else if (width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
b = _Float_FToF(8, 4, arg);
} else if (width == 64) {
b = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
}
} else {
if (Op->Src[0].IsNone()) {
// Implicit arg
auto offset = _Constant(Op->OP & 7);
arg = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, offset), mask);
b = _LoadContextIndexed(arg, 8, MMBaseOffset(), 16, FPRClass);
uint8_t offset = Op->OP & 7;
b = _ReadStackValue(offset);
} else {
// Memory arg
if (Width == 16 || Width == 32 || Width == 64) {
if (Integer) {
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags);
if (Width == 16) {
arg = _Sbfe(OpSize::i64Bit, 16, 0, arg);
}
b = _Float_FromGPR_S(8, Width == 64 ? 8 : 4, arg);
} else if (Width == 32) {
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
b = _Float_FToF(8, 4, arg);
} else if (Width == 64) {
b = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags);
}
}
}
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
if (whichflags == FCOMIFlags::FLAGS_X87) {
if (WhichFlags == FCOMIFlags::FLAGS_X87) {
// We are going to clobber NZCV, make sure it's in a GPR first.
GetNZCV();
_FCmp(8, a, b);
_F80CmpValue(b);
PossiblySetNZCVBits = ~0;
ConvertNZCVToX87();
} else {
Comiss(8, a, b, true /* InvalidateAF */);
HandleNZCVWrite();
_F80CmpValue(b);
ComissFlags(true /* InvalidateAF */);
}
if (poptwice) {
// if we are popping then we must first mark this location as empty
SetX87ValidTag(top, false);
top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, _Constant(1)), mask);
SetX87ValidTag(top, false);
// Set the new top now
top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, _Constant(1)), mask);
SetX87Top(top);
if (PopTwice) {
_PopStackDestroy();
_PopStackDestroy();
} else if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
// if we are popping then we must first mark this location as empty
SetX87ValidTag(top, false);
// Set the new top now
top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, _Constant(1)), mask);
SetX87Top(top);
_PopStackDestroy();
}
}
void OpDispatchBuilder::FSQRTF64(OpcodeArgs) {
auto top = GetX87Top();
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
auto result = _VFSqrt(8, 8, a);
// Write to ST[TOP]
_StoreContextIndexed(result, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::X87UnaryOpF64(OpcodeArgs, FEXCore::IR::IROps IROp) {
auto top = GetX87Top();
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
DeriveOp(result, IROp, _F64SIN(a));
if (IROp == IR::OP_F64SIN || IROp == IR::OP_F64COS) {
// TODO: ACCURACY: should check source is in range –2^63 to +2^63
SetRFLAG<FEXCore::X86State::X87FLAG_C2_LOC>(_Constant(0));
}
// Write to ST[TOP]
_StoreContextIndexed(result, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::X87BinaryOpF64(OpcodeArgs, FEXCore::IR::IROps IROp) {
auto top = GetX87Top();
auto mask = _Constant(7);
Ref st1 = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, top, _Constant(1)), mask);
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
st1 = _LoadContextIndexed(st1, 8, MMBaseOffset(), 16, FPRClass);
DeriveOp(result, IROp, _F64ATAN(a, st1));
if (IROp == IR::OP_F64FPREM || IROp == IR::OP_F64FPREM1) {
// TODO: Set C0 to Q2, C3 to Q1, C1 to Q0
SetRFLAG<FEXCore::X86State::X87FLAG_C2_LOC>(_Constant(0));
}
// Write to ST[TOP]
_StoreContextIndexed(result, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::X87SinCosF64(OpcodeArgs) {
auto orig_top = GetX87Top();
auto top = _And(OpSize::i32Bit, _Sub(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87ValidTag(top, true);
SetX87Top(top);
auto a = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
auto sin = _F64SIN(a);
auto cos = _F64COS(a);
// TODO: ACCURACY: should check source is in range –2^63 to +2^63
SetRFLAG<FEXCore::X86State::X87FLAG_C2_LOC>(_Constant(0));
// Write to ST[TOP]
_StoreContextIndexed(sin, orig_top, 8, MMBaseOffset(), 16, FPRClass);
_StoreContextIndexed(cos, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::X87FYL2XF64(OpcodeArgs) {
bool Plus1 = Op->OP == 0x01F9; // FYL2XP
auto orig_top = GetX87Top();
// if we are popping then we must first mark this location as empty
SetX87ValidTag(orig_top, false);
auto top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87Top(top);
Ref st0 = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
Ref st1 = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
if (Plus1) {
auto one = _VCastFromGPR(8, 8, _Constant(0x3FF0000000000000));
st0 = _VFAdd(8, 8, st0, one);
}
auto result = _F64FYL2X(st0, st1);
// Write to ST[TOP]
_StoreContextIndexed(result, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::X87TANF64(OpcodeArgs) {
auto orig_top = GetX87Top();
auto top = _And(OpSize::i32Bit, _Sub(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87ValidTag(top, true);
SetX87Top(top);
auto a = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
auto result = _F64TAN(a);
auto one = _VCastFromGPR(8, 8, _Constant(0x3FF0000000000000));
// TODO: ACCURACY: should check source is in range –2^63 to +2^63
SetRFLAG<FEXCore::X86State::X87FLAG_C2_LOC>(_Constant(0));
// Write to ST[TOP]
_StoreContextIndexed(result, orig_top, 8, MMBaseOffset(), 16, FPRClass);
_StoreContextIndexed(one, top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::X87ATANF64(OpcodeArgs) {
auto orig_top = GetX87Top();
// if we are popping then we must first mark this location as empty
SetX87ValidTag(orig_top, false);
auto top = _And(OpSize::i32Bit, _Add(OpSize::i32Bit, orig_top, _Constant(1)), _Constant(7));
SetX87Top(top);
auto a = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
Ref st1 = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
auto result = _F64ATAN(st1, a);
// Write to ST[TOP]
_StoreContextIndexed(result, top, 8, MMBaseOffset(), 16, FPRClass);
}
// This function converts to F80 on save for compatibility
void OpDispatchBuilder::X87FNSAVEF64(OpcodeArgs) {
_SyncStackToSlow();
// 14 bytes for 16bit
// 2 Bytes : FCW
// 2 Bytes : FSW
@@ -717,13 +445,13 @@ void OpDispatchBuilder::X87FNSAVEF64(OpcodeArgs) {
_StoreMem(GPRClass, Size, Mem, FCW, Size);
}
{ _StoreMem(GPRClass, Size, ReconstructFSW(), Mem, _Constant(Size * 1), Size, MEM_OFFSET_SXTX, 1); }
{ _StoreMem(GPRClass, Size, ReconstructFSW_Helper(), Mem, _Constant(Size * 1), Size, MEM_OFFSET_SXTX, 1); }
auto ZeroConst = _Constant(0);
{
// FTW
_StoreMem(GPRClass, Size, GetX87FTW(), Mem, _Constant(Size * 2), Size, MEM_OFFSET_SXTX, 1);
_StoreMem(GPRClass, Size, GetX87FTW_Helper(), Mem, _Constant(Size * 2), Size, MEM_OFFSET_SXTX, 1);
}
{
@@ -772,6 +500,7 @@ void OpDispatchBuilder::X87FNSAVEF64(OpcodeArgs) {
// This function converts from F80 on load for compatibility
void OpDispatchBuilder::X87FRSTORF64(OpcodeArgs) {
_StackForceSlow();
const auto Size = GetSrcSize(Op);
Ref Mem = MakeSegmentAddress(Op, Op->Src[0]);
@@ -788,7 +517,7 @@ void OpDispatchBuilder::X87FRSTORF64(OpcodeArgs) {
_StoreContext(2, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
auto NewFSW = _LoadMem(GPRClass, Size, Mem, _Constant(Size * 1), Size, MEM_OFFSET_SXTX, 1);
auto Top = ReconstructX87StateFromFSW(NewFSW);
Ref Top = ReconstructX87StateFromFSW_Helper(NewFSW);
{
// FTW
@@ -826,32 +555,4 @@ void OpDispatchBuilder::X87FRSTORF64(OpcodeArgs) {
_StoreContextIndexed(Reg, Top, 8, MMBaseOffset(), 16, FPRClass);
}
// FXAM needs change
void OpDispatchBuilder::X87FXAMF64(OpcodeArgs) {
auto top = GetX87Top();
auto a = _LoadContextIndexed(top, 8, MMBaseOffset(), 16, FPRClass);
Ref Result = _VExtractToGPR(8, 8, a, 0);
// Extract the sign bit
Result = _Bfe(OpSize::i64Bit, 1, 63, Result);
SetRFLAG<FEXCore::X86State::X87FLAG_C1_LOC>(Result);
// Claim this is a normal number
// We don't support anything else
auto TopValid = GetX87ValidTag(top);
auto ZeroConst = _Constant(0);
auto OneConst = _Constant(1);
// In the case of top being invalid then C3:C2:C0 is 0b101
auto C3 = _Select(FEXCore::IR::COND_EQ, TopValid, OneConst, ZeroConst, OneConst);
auto C2 = TopValid;
auto C0 = C3; // Mirror C3 until something other than zero is supported
SetRFLAG<FEXCore::X86State::X87FLAG_C0_LOC>(C0);
SetRFLAG<FEXCore::X86State::X87FLAG_C2_LOC>(C2);
SetRFLAG<FEXCore::X86State::X87FLAG_C3_LOC>(C3);
}
} // namespace FEXCore::IR
@@ -70,6 +70,7 @@ void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl* ctx) {
FEX_CONFIG_OPT(DisablePasses, O0);
if (!DisablePasses()) {
InsertPass(CreateX87StackOptimizationPass());
InsertPass(CreateDeadStoreElimination());
InsertPass(CreateConstProp(ctx->HostFeatures.SupportsTSOImm9, &ctx->CPUID));
InsertPass(CreateDeadFlagCalculationEliminination());
+1
View File
@@ -20,6 +20,7 @@ fextl::unique_ptr<FEXCore::IR::Pass> CreateConstProp(bool SupportsTSOImm9, const
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadFlagCalculationEliminination();
fextl::unique_ptr<FEXCore::IR::Pass> CreateDeadStoreElimination();
fextl::unique_ptr<FEXCore::IR::RegisterAllocationPass> CreateRegisterAllocationPass();
fextl::unique_ptr<FEXCore::IR::Pass> CreateX87StackOptimizationPass();
namespace Validation {
fextl::unique_ptr<FEXCore::IR::Pass> CreateIRValidation();
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -5,7 +5,7 @@ See [FEXCore/Readme.md](../FEXCore/Readme.md) for more details
### Glossary
- Splatter: a code generator backend that concaternates configurable macros instead of doing isel
- Splatter: a code generator backend that concatenates configurable macros instead of doing isel
- IR: Intermediate Representation, our high-level opcode representation, loosely modeling arm64
- SSA: Single Static Assignment, a form of representing IR in memory
- Basic Block: A block of instructions with no control flow, terminated by control flow
@@ -115,6 +115,7 @@ IR to IR Optimization
- [RedundantFlagCalculationElimination.cpp](../FEXCore/Source/Interface/IR/Passes/RedundantFlagCalculationElimination.cpp): This is not used right now, possibly broken
- [RegisterAllocationPass.cpp](../FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.cpp)
- [RegisterAllocationPass.h](../FEXCore/Source/Interface/IR/Passes/RegisterAllocationPass.h)
- [x87StackOptimizationPass.cpp](../FEXCore/Source/Interface/IR/Passes/x87StackOptimizationPass.cpp): x87 stack optimization pass
+1
View File
@@ -0,0 +1 @@
Test_X87/FXAM_Simple.asm
+1 -1
View File
@@ -11,7 +11,7 @@ mov rdx, 0xe0000000
; This behaviour was seen around Wine 32-bit libraries
; Anything doing a call to a double application would spin
; the x87 stack on to the stack looking for fxam to return empty
; Empty in this case is that C0 and C3 is set whiel C2 is not
; Empty in this case is that C0 and C3 is set while C2 is not
fninit
; Fill the x87 stack
+1 -1
View File
@@ -11,7 +11,7 @@ mov rdx, 0xe0000000
; This behaviour was seen around Wine 32-bit libraries
; Anything doing a call to a double application would spin
; the x87 stack on to the stack looking for fxam to return empty
; Empty in this case is that C0 and C3 is set whiel C2 is not
; Empty in this case is that C0 and C3 is set while C2 is not
fninit
; Empty stack to make sure we don't push anything
+40
View File
@@ -0,0 +1,40 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "8"
}
}
%endif
fninit
fld1
fld1
fld1
fld1
fld1
fld1
fld1
fld1
mov ebx, 0
.ExamineStack:
; Examine st(0)
fxam
fwait
; Get the results in to AX
fnstsw ax
and ax, 0x4500
; Check for empty
cmp ax, 0x4100
je .Done
; Now push the x87 stack value
; We know it isn't empty
fstp st0
fwait
inc ebx
jmp .ExamineStack
.Done:
mov eax, ebx
hlt
+51
View File
@@ -0,0 +1,51 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "8"
}
}
%endif
mov rdx, 0xe0000000
; This behaviour was seen around Wine 32-bit libraries
; Anything doing a call to a double application would spin
; the x87 stack on to the stack looking for fxam to return empty
; Empty in this case is that C0 and C3 is set while C2 is not
fninit
; Fill the x87 stack
fldz
fldz
fldz
fldz
fldz
fldz
fldz
fldz
mov eax, 0
mov ecx, 0
.ExamineStack:
; Examine st(0)
fxam
fwait
; Get the results in to AX
fnstsw ax
and ax, 0x4500
; Check for empty
cmp ax, 0x4100
je .Done
; Now push the x87 stack value
; We know it isn't empty
fstp qword [rdx + rcx * 8]
fwait
inc ecx
jmp .ExamineStack
.Done:
; Save how many we stored
mov eax, ecx
hlt
+49
View File
@@ -0,0 +1,49 @@
;; Simpler versions of FXAM_Push* tests.
;; In hostrunner tests this will fail because we mentioned below there's no support
;; for the zero flag. In hostrunner RCX should contain 0x4000 instead of 0x400.
%ifdef CONFIG
{
"RegData": {
"RAX": "0x6",
"RBX": "0x0400",
"RCX": "0x0400",
"RDX": "0x4100"
}
}
%endif
mov rdx, 0xe0000000
fninit
;; Before adding anything to the stack, lets examine it.
;; The result should be empty.
fxam
fwait
fnstsw ax
and ax, 0x4500 ; should be 0x4100 for zero
mov edx, eax
fldz
fxam
fwait
fnstsw ax
and ax, 0x4500 ; should be 0x4000 for zero, but there's no support for it at the moment, so it'll return 0x0400 as it does for a normal number.
mov ecx, eax
fld1
fxam
fwait
fnstsw ax
mov ebx, eax
and ebx, 0x4500 ; should be 0x0400 for normal
;; Top should be 6
;; right shift status word by 11 and and with 0x7.
shr eax, 11
and eax, 0x7
hlt
+21
View File
@@ -0,0 +1,21 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0x3ff8000000000000",
"RBX": "0x3ff8000000000000"
},
"MemoryRegions": {
"0x100000000": "4096"
}
}
%endif
mov rdx, 0x100000000
mov rax, 0x3ff8000000000000 ; 1.5
mov [rdx], rax
fld qword [rdx]
fstp qword [rdx + 8]
mov rbx, [rdx + 8]
hlt
+26
View File
@@ -0,0 +1,26 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0x4142434445464748",
"RBX": "0"
}
}
%endif
lea rax, [rel .data]
lea rbx, [rel .data_mov]
fld qword [rax]
fstp qword [rbx]
mov rax, [rbx]
mov rbx, [rbx + 8]
hlt
.data:
dq 0x4142434445464748
dq 0x5152535455565758
.data_mov:
dq 0
dq 0
@@ -0,0 +1,2 @@
## Unable to support zero flag
FXAM_Simple.asm
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,5 +1,4 @@
pr72867.c.gcc-target-test-32
pr88240.c.gcc-target-test-32
sse2-mmx-pextrw.c.gcc-target-test-32
# Consistently crashes on Solidrun only in CI
@@ -13,15 +13,6 @@ asm-5.c.gcc-target-test-64
# This causes its comparison to fail
sse2-mmx-pextrw.c.gcc-target-test-64
# Fails even on host device
# Stores a large 64bit value in to a union of double and 'unsigned long long'
# 'test' loads this value in to a x87 register, then stores that value on to the stack
# Checks the flag and skips a bunch of x87 logic, and loads that value back from the stack in to rax
# Value has been munged from the fld + fstp step
# 0xFFF279535D540FE4 was the original value
# 0xFFFA79535D540FE4 was the value it turned in to
pr88240.c.gcc-target-test-64
# These tests fail because of things unrelated to the sse4.1 instructions
sse4_1-ceil-sfix-vec.c.gcc-target-test-64
sse4_1-ceilf-sfix-vec.c.gcc-target-test-64