diff --git a/FEXCore/Scripts/json_ir_generator.py b/FEXCore/Scripts/json_ir_generator.py index 12194631f..64a8f6809 100755 --- a/FEXCore/Scripts/json_ir_generator.py +++ b/FEXCore/Scripts/json_ir_generator.py @@ -125,21 +125,36 @@ def parse_ops(ops): RHS = EqualSplit[0].strip() if len(EqualSplit) > 1: - OpDef.HasDest = True + LHS = EqualSplit[0].strip() RHS = EqualSplit[1].strip() - # Parse the destination, must be one type of SSA, GPR, or FPR - ResultType = EqualSplit[0].strip() - if ResultType == "SSA": - OpDef.DestType = "SSA" # We don't know this type right now - elif ResultType == "GPR": - OpDef.DestType = "GPR" - elif ResultType == "GPRPair": - OpDef.DestType = "GPRPair" - elif ResultType == "FPR": - OpDef.DestType = "FPR" + if ":" in LHS: + # Named destinations. This is a hack, but so is the entire + # multi-destination support bolten onto the old IR... + # + # Named destinations require side effects because they break + # SSA hard. Validate that. + assert("HasSideEffects" in op_val and op_val["HasSideEffects"]) + + for Dest in LHS.split(","): + Dest = Dest.strip() + DType, Name = Dest.split(":$") + + # If the destination appears also as a source, it is + # read-modify-write. + if Dest in RHS: + # Turn RMW into an in/out source + RHS = RHS.replace(Dest.strip(), f"{DType}:$Inout{Name}") + else: + # Turn named destinations into an out source. + RHS += f", {DType}:$Out{Name}" else: - ExitError("Unknown destination class type {}. Needs to be one of {SSA, GPR, GPRPair, FPR}".format(ResultType)) + # Single anonymous destination + if LHS not in ["SSA", "GPR", "GPRPair", "FPR"]: + ExitError(f"Unknown destination class type {LHS}. Needs to be one of SSA, GPR, GPRPair, FPR") + + OpDef.HasDest = True + OpDef.DestType = LHS # IR Op needs to start with a name RHS = RHS.split(" ", 1) diff --git a/FEXCore/Source/Interface/Core/JIT/Arm64/MemoryOps.cpp b/FEXCore/Source/Interface/Core/JIT/Arm64/MemoryOps.cpp index 619fb2902..cf0b725ee 100644 --- a/FEXCore/Source/Interface/Core/JIT/Arm64/MemoryOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/Arm64/MemoryOps.cpp @@ -1365,7 +1365,7 @@ DEF_OP(Push) { DEF_OP(Pop) { const auto Op = IROp->C(); const auto Addr = GetReg(Op->InoutAddr.ID()); - const auto Dst = GetReg(Node); + const auto Dst = GetReg(Op->OutValue.ID()); LOGMAN_THROW_A_FMT(Dst != Addr, "Invalid"); diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 7cfd09728..ab5909f94 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -506,8 +506,8 @@ void OpDispatchBuilder::PUSHSegmentOp(OpcodeArgs) { } void OpDispatchBuilder::POPOp(OpcodeArgs) { - auto SP = _RMWHandle(LoadGPRRegister(X86State::REG_RSP)); - auto Value = _Pop(GetSrcSize(Op), SP); + Ref SP = _RMWHandle(LoadGPRRegister(X86State::REG_RSP)); + Ref Value = Pop(GetSrcSize(Op), SP); // Store the new stack pointer and what we loaded from the stack StoreGPRRegister(X86State::REG_RSP, SP); @@ -520,17 +520,17 @@ void OpDispatchBuilder::POPAOp(OpcodeArgs) { Ref SP = _RMWHandle(LoadGPRRegister(X86State::REG_RSP)); - StoreGPRRegister(X86State::REG_RDI, _Pop(Size, SP), Size); - StoreGPRRegister(X86State::REG_RSI, _Pop(Size, SP), Size); - StoreGPRRegister(X86State::REG_RBP, _Pop(Size, SP), Size); + StoreGPRRegister(X86State::REG_RDI, Pop(Size, SP), Size); + StoreGPRRegister(X86State::REG_RSI, Pop(Size, SP), Size); + StoreGPRRegister(X86State::REG_RBP, Pop(Size, SP), Size); // Skip loading RSP because it'll be correct at the end SP = _RMWHandle(_Add(OpSize::i64Bit, SP, _Constant(Size))); - StoreGPRRegister(X86State::REG_RBX, _Pop(Size, SP), Size); - StoreGPRRegister(X86State::REG_RDX, _Pop(Size, SP), Size); - StoreGPRRegister(X86State::REG_RCX, _Pop(Size, SP), Size); - StoreGPRRegister(X86State::REG_RAX, _Pop(Size, SP), Size); + StoreGPRRegister(X86State::REG_RBX, Pop(Size, SP), Size); + StoreGPRRegister(X86State::REG_RDX, Pop(Size, SP), Size); + StoreGPRRegister(X86State::REG_RCX, Pop(Size, SP), Size); + StoreGPRRegister(X86State::REG_RAX, Pop(Size, SP), Size); // Store the new stack pointer StoreGPRRegister(X86State::REG_RSP, SP); @@ -542,7 +542,7 @@ void OpDispatchBuilder::POPSegmentOp(OpcodeArgs) { const uint8_t DstSize = GetDstSize(Op); Ref SP = _RMWHandle(LoadGPRRegister(X86State::REG_RSP)); - auto NewSegment = _Pop(SrcSize, SP); + auto NewSegment = Pop(SrcSize, SP); // Store the new stack pointer StoreGPRRegister(X86State::REG_RSP, SP); @@ -575,7 +575,7 @@ void OpDispatchBuilder::POPSegmentOp(OpcodeArgs) { void OpDispatchBuilder::LEAVEOp(OpcodeArgs) { // First we move RBP in to RSP and then behave effectively like a pop auto SP = _RMWHandle(LoadGPRRegister(X86State::REG_RBP)); - auto NewGPR = _Pop(GetSrcSize(Op), SP); + auto NewGPR = Pop(GetSrcSize(Op), SP); // Store the new stack pointer StoreGPRRegister(X86State::REG_RSP, SP); diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index ed80e836d..18f34dd13 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -2383,6 +2383,12 @@ private: return _Prefetch(ForStore, Stream, CacheLevel, ssa0, Invalid(), MEM_OFFSET_SXTX, 1); } + Ref Pop(uint8_t Size, Ref SP_RMW) { + Ref Value = _AllocateGPR(false); + _Pop(Size, SP_RMW, Value); + return Value; + } + void InstallHostSpecificOpcodeHandlers(); ///< Segment telemetry tracking diff --git a/FEXCore/Source/Interface/IR/IR.json b/FEXCore/Source/Interface/IR/IR.json index a82aa7799..c1b0857af 100644 --- a/FEXCore/Source/Interface/IR/IR.json +++ b/FEXCore/Source/Interface/IR/IR.json @@ -324,17 +324,13 @@ "HasSideEffects": true }, - "CPUID GPR:$Function, GPR:$Leaf, GPR:$OutEAX, GPR:$OutEBX, GPR:$OutECX, GPR:$OutEDX": { - "Desc": ["Calls in to the CPUID handler function to return emulated CPUID", - "Returns GPRs for emulated EAX, EBX, EDX, ECX respectively" - ], + "GPR:$EAX, GPR:$EBX, GPR:$ECX, GPR:$EDX = CPUID GPR:$Function, GPR:$Leaf": { + "Desc": ["Calls in to the CPUID handler function to return emulated CPUID"], "DestSize": "4", "HasSideEffects": true }, - "XGetBV GPR:$Function, GPR:$OutEAX, GPR:$OutEDX": { - "Desc": ["Calls in to the XCR handler function to return emulated XCR", - "Returns emulated EAX, EDX." - ], + "GPR:$EAX, GPR:$EDX = XGetBV GPR:$Function": { + "Desc": ["Calls in to the XCR handler function to return emulated XCR"], "DestSize": "4", "HasSideEffects": true } @@ -593,10 +589,10 @@ "HasSideEffects": true, "TiedSource": 0 }, - "GPR = Pop u8:$Size, GPR:$InoutAddr": { + "GPR:$Value, GPR:$Addr = Pop u8:$Size, GPR:$Addr": { "Desc": [ - "Pops a value to the address, updating the new pointer after incrementing.", - "The address is decremented by the size." + "Pops a value from the address, updating the new pointer after incrementing.", + "The address is incremented by the size via an RMW source/destintaion." ], "HasSideEffects": true, "DestSize": "Size" @@ -608,9 +604,9 @@ "HasSideEffects": true, "DestSize": "8" }, - "MemCpy i1:$IsAtomic, u8:$Size, GPR:$Dest, GPR:$Src, GPR:$Length, GPR:$Direction, GPR:$OutDstAddress, GPR:$OutSrcAddress": { + "GPR:$DstAddress, GPR:$SrcAddress = MemCpy i1:$IsAtomic, u8:$Size, GPR:$Dest, GPR:$Src, GPR:$Length, GPR:$Direction": { "Desc": ["Duplicates behaviour of x86 MOVS repeat", - "Returns the final addresses of destination and src addresses after they have been incremented or decremented" + "Returns the final addresses after they have been incremented or decremented" ], "HasSideEffects": true, "DestSize": "8" @@ -710,13 +706,12 @@ "Size == FEXCore::IR::OpSize::i8Bit || Size == FEXCore::IR::OpSize::i16Bit || Size == FEXCore::IR::OpSize::i32Bit || Size == FEXCore::IR::OpSize::i64Bit" ] }, - "CASPair OpSize:#Size, GPR:$ExpectedLo, GPR:$ExpectedHi, GPR:$DesiredLo, GPR:$DesiredHi, GPR:$Addr, GPR:$OutLo, GPR:$OutHi": { + "GPR:$Lo, GPR:$Hi = CASPair OpSize:#Size, GPR:$ExpectedLo, GPR:$ExpectedHi, GPR:$DesiredLo, GPR:$DesiredHi, GPR:$Addr": { "Desc": ["Does a compare and exchange with two pairs of values", "ssa0 is the comparison value", "ssa1 is the new value", "ssa2 is the memory location", - "Returns the lower & upper halves of the value in memory, via out sources.", - "We use 2 out sources to help the pair coalesce." + "Returns the lower & upper halves of the value in memory." ], "HasSideEffects": true, "DestSize": "Size",