From a27e004b17c107295b6d66e5b633f51299cec26b Mon Sep 17 00:00:00 2001 From: Marvis Date: Sat, 1 Aug 2026 18:46:07 +0200 Subject: [PATCH 1/8] Fix mtmsr and mtmsrd decoding --- arch/powerpc/decode/decode.c | 4 ++-- arch/powerpc/decode/operands.c | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/decode/decode.c b/arch/powerpc/decode/decode.c index fc5dc9d5bc..da22feed7e 100644 --- a/arch/powerpc/decode/decode.c +++ b/arch/powerpc/decode/decode.c @@ -2912,7 +2912,7 @@ InstructionId Decode0x1F(uint32_t word32, uint32_t decodeFlags) return PPC_ID_MTCRF; case 0x124: - if ((a != 0) || (b != 0)) + if (((a & 0x1e) != 0) || (b != 0)) return PPC_ID_INVALID; return PPC_ID_MTMSR; @@ -2948,7 +2948,7 @@ InstructionId Decode0x1F(uint32_t word32, uint32_t decodeFlags) return PPC_ID_INVALID; case 0x164: - if ((a != 0) || (b != 0)) + if (((a & 0x1e) != 0) || (b != 0)) return PPC_ID_INVALID; return PPC_ID_MTMSRD; diff --git a/arch/powerpc/decode/operands.c b/arch/powerpc/decode/operands.c index 3350737afd..8905ab5e59 100644 --- a/arch/powerpc/decode/operands.c +++ b/arch/powerpc/decode/operands.c @@ -380,13 +380,24 @@ void FillOperands32(Instruction* instruction, uint32_t word32, uint64_t address) case PPC_ID_MTBR7: case PPC_ID_MTCTR: case PPC_ID_MTLR: - case PPC_ID_MTMSR: - case PPC_ID_MTMSRD: case PPC_ID_MTXER: case PPC_ID_WRTEE: PushRS(instruction, word32); break; + // rS [, L] + case PPC_ID_MTMSR: + case PPC_ID_MTMSRD: + { + uint32_t l = (word32 >> 16) & 0x1; + + PushRS(instruction, word32); + if (l != 0) + PushUIMMValue(instruction, l); + + break; + } + // rA case PPC_ID_TABORT: case PPC_ID_TRECLAIM: From cd956e50a8dc8a55f66dce171470a630f819c643 Mon Sep 17 00:00:00 2001 From: Marvis Date: Sat, 1 Aug 2026 20:52:26 +0200 Subject: [PATCH 2/8] Fixed count trailing zeros and added more variations in PowerPC --- arch/powerpc/arch_ppc.cpp | 10 ++-------- arch/powerpc/il.cpp | 29 ++++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/arch_ppc.cpp b/arch/powerpc/arch_ppc.cpp index ef6f7dc417..99af53f88a 100644 --- a/arch/powerpc/arch_ppc.cpp +++ b/arch/powerpc/arch_ppc.cpp @@ -665,8 +665,6 @@ class PowerpcArchitecture: public Architecture { switch (intrinsic) { - case PPC_INTRIN_CNTLZW: - return "__builtin_clz"; case PPC_INTRIN_FRSP: return "float_round"; default: @@ -688,11 +686,11 @@ class PowerpcArchitecture: public Architecture // Double check someone didn't insert a new intrinsic at the beginning of our enum since we rely // on it to fill the next array. - static_assert(PPCIntrinsic::PPC_INTRIN_CNTLZW == 0, + static_assert(PPCIntrinsic::PPC_INTRIN_FRSP == 0, "Invalid first PPCIntrinsic value. Please add your intrinsic further in the enum."); // Normal intrinsics. - for (uint32_t id = PPC_INTRIN_CNTLZW; id < PPCIntrinsic::PPC_INTRIN_END; id++) { + for (uint32_t id = PPC_INTRIN_FRSP; id < PPCIntrinsic::PPC_INTRIN_END; id++) { result.push_back(id); } @@ -724,8 +722,6 @@ class PowerpcArchitecture: public Architecture { switch (intrinsic) { - case PPC_INTRIN_CNTLZW: // rs - return {NameAndType(Type::IntegerType(4, false))}; case PPC_INTRIN_FRSP: return {NameAndType(Type::FloatType(4))}; // for now, quantize is operating on the float in, and the gqr that holds the scale @@ -758,8 +754,6 @@ class PowerpcArchitecture: public Architecture { switch (intrinsic) { - case PPC_INTRIN_CNTLZW: // ra - return {Type::IntegerType(4, false)}; case PPC_INTRIN_FRSP: return {Type::FloatType(4)}; default: diff --git a/arch/powerpc/il.cpp b/arch/powerpc/il.cpp index 043acf08ac..2ecf49c0c0 100644 --- a/arch/powerpc/il.cpp +++ b/arch/powerpc/il.cpp @@ -2279,10 +2279,33 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(ei1); break; + // count leading/trailing zeros: [.] rA, rS case PPC_ID_CNTLZWx: - ei0 = il.Intrinsic({RegisterOrFlag::Register(oper1->reg)}, PPC_INTRIN_CNTLZW, - {operToIL(il, oper0)}); - il.AddInstruction(ei0); + REQUIRE2OPS + ei0 = il.CountLeadingZeros(4, il.Register(4, oper1->reg)); + il.AddInstruction(il.SetRegister(4, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0)); + break; + + case PPC_ID_CNTLZDx: + REQUIRE2OPS + ei0 = il.CountLeadingZeros(8, il.Register(8, oper1->reg)); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0)); + break; + + case PPC_ID_CNTTZWx: + REQUIRE2OPS + ei0 = il.CountTrailingZeros(4, il.Register(4, oper1->reg)); + il.AddInstruction(il.SetRegister(4, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0)); + break; + + case PPC_ID_CNTTZDx: + REQUIRE2OPS + ei0 = il.CountTrailingZeros(8, il.Register(8, oper1->reg)); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0)); break; case PPC_ID_PAIREDSINGLE_PSQ_ST: From b7233206929ff08533ecccb13ce0a6652e72bef6 Mon Sep 17 00:00:00 2001 From: Marvis Date: Sat, 1 Aug 2026 20:53:19 +0200 Subject: [PATCH 3/8] Added lifting to more PowerPC instructions --- arch/powerpc/arch_ppc.cpp | 9 + arch/powerpc/il.cpp | 337 +++++++++++++++++++++++++++++++++++++- arch/powerpc/il.h | 3 +- 3 files changed, 343 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/arch_ppc.cpp b/arch/powerpc/arch_ppc.cpp index 99af53f88a..bf2758d6a0 100644 --- a/arch/powerpc/arch_ppc.cpp +++ b/arch/powerpc/arch_ppc.cpp @@ -667,6 +667,10 @@ class PowerpcArchitecture: public Architecture { case PPC_INTRIN_FRSP: return "float_round"; + case PPC_INTRIN_SET_RESERVATION: + return "__set_reservation"; + case PPC_INTRIN_CHECK_RESERVATION: + return "__check_reservation"; default: if ((decodeFlags & DECODE_FLAGS_PS)) { @@ -724,6 +728,9 @@ class PowerpcArchitecture: public Architecture { case PPC_INTRIN_FRSP: return {NameAndType(Type::FloatType(4))}; + case PPC_INTRIN_SET_RESERVATION: + case PPC_INTRIN_CHECK_RESERVATION: + return {NameAndType("address", Type::IntegerType(addressSize, false))}; // for now, quantize is operating on the float in, and the gqr that holds the scale default: if ((decodeFlags & DECODE_FLAGS_PS)) @@ -756,6 +763,8 @@ class PowerpcArchitecture: public Architecture { case PPC_INTRIN_FRSP: return {Type::FloatType(4)}; + case PPC_INTRIN_CHECK_RESERVATION: + return {Type::IntegerType(0, false)}; default: if ((decodeFlags & DECODE_FLAGS_PS)) { diff --git a/arch/powerpc/il.cpp b/arch/powerpc/il.cpp index 2ecf49c0c0..c1839019da 100644 --- a/arch/powerpc/il.cpp +++ b/arch/powerpc/il.cpp @@ -500,6 +500,174 @@ static void load_float(LowLevelILFunction& il, } } +/* stwcx./stdcx. set CR0 = 0b00 || store_performed || XER[SO]. */ +static void SetStoreConditionalCR0(LowLevelILFunction& il, bool succeeded) +{ + il.AddInstruction(il.SetFlag(IL_FLAG_LT, il.Const(0, 0))); + il.AddInstruction(il.SetFlag(IL_FLAG_GT, il.Const(0, 0))); + il.AddInstruction(il.SetFlag(IL_FLAG_EQ, il.Const(0, succeeded ? 1 : 0))); + il.AddInstruction(il.SetFlag(IL_FLAG_SO, il.Flag(IL_FLAG_XER_SO))); +} + +/* Effective address (rA|0) + rB shared by the reservation instructions. */ +static ExprId ReservationAddress(LowLevelILFunction& il, Operand* ra, Operand* rb, size_t addressSize) +{ + return il.Add(addressSize, + operToIL(il, ra, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize), + operToIL_a(il, rb, addressSize)); +} + +/* stwcx./stdcx.: the reservation status is provided by an intrinsic, and the + store plus its CR0 result are conditional on it. This mirrors how the mips + (ll/sc) and armv7/arm64 (ldrex/strex) lifters model store-conditional. */ +static void LiftStoreConditional(LowLevelILFunction& il, Instruction* instruction, size_t size, size_t addressSize) +{ + Operand* rs = &instruction->operands[0]; + Operand* ra = &instruction->operands[1]; + Operand* rb = &instruction->operands[2]; + + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(LLIL_TEMP(0))}, + PPC_INTRIN_CHECK_RESERVATION, {ReservationAddress(il, ra, rb, addressSize)})); + + LowLevelILLabel success, fail, done; + il.AddInstruction(il.If( + il.CompareEqual(0, il.Register(0, LLIL_TEMP(0)), il.Const(0, 1)), success, fail)); + + // reservation held: perform the store and report success in CR0 + il.MarkLabel(success); + ExprId value = (size < addressSize) + ? il.LowPart(size, operToIL_a(il, rs, addressSize)) + : operToIL_a(il, rs, size); + il.AddInstruction(il.Store(size, ReservationAddress(il, ra, rb, addressSize), value)); + SetStoreConditionalCR0(il, true); + il.AddInstruction(il.Goto(done)); + + // reservation lost: no store, CR0 reports failure + il.MarkLabel(fail); + SetStoreConditionalCR0(il, false); + + il.MarkLabel(done); +} + +/* Map a condition-encoding trap mnemonic (e.g. "tdlgt") to its 5-bit TO field. */ +static uint32_t TrapConditionMask(uint32_t id) +{ + switch (id) + { + case PPC_ID_TDLGT: case PPC_ID_TDLGTI: + case PPC_ID_TWLGT: case PPC_ID_TWLGTI: return 0x01; // a >u b + case PPC_ID_TDLLT: case PPC_ID_TDLLTI: + case PPC_ID_TWLLT: case PPC_ID_TWLLTI: return 0x02; // a s b + case PPC_ID_TDLT: case PPC_ID_TDLTI: + case PPC_ID_TWLT: case PPC_ID_TWLTI: return 0x10; // a =s b + case PPC_ID_TWLEI: return 0x14; // a <=s b + case PPC_ID_TWLLEI: return 0x06; // a <=u b + default: return 0x00; + } +} + +/* Build the boolean condition for a trap from its TO field. TO bit meanings: + 0x10 = s, 0x04 = ==, 0x02 = u. */ +static ExprId TrapCondition(LowLevelILFunction& il, uint32_t to, Operand* aOp, Operand* bOp, size_t size) +{ + ExprId a = operToIL_a(il, aOp, size); + ExprId b = operToIL_a(il, bOp, size); + + // Every trap mnemonic the decoder produces maps to a single comparison. + switch (to & 0x1f) + { + case 0x01: return il.CompareUnsignedGreaterThan(size, a, b); + case 0x02: return il.CompareUnsignedLessThan(size, a, b); + case 0x03: return il.CompareNotEqual(size, a, b); // u + case 0x04: return il.CompareEqual(size, a, b); + case 0x05: return il.CompareUnsignedGreaterEqual(size, a, b); + case 0x06: return il.CompareUnsignedLessEqual(size, a, b); + case 0x08: return il.CompareSignedGreaterThan(size, a, b); + case 0x0c: return il.CompareSignedGreaterEqual(size, a, b); + case 0x10: return il.CompareSignedLessThan(size, a, b); + case 0x14: return il.CompareSignedLessEqual(size, a, b); + case 0x18: return il.CompareNotEqual(size, a, b); // s + default: break; + } + + // General case (arbitrary TO on a generic td/tw): OR the enabled comparisons. + static const uint32_t bits[5] = { 0x10, 0x08, 0x04, 0x02, 0x01 }; + ExprId cond = BN_INVALID_EXPR; + bool first = true; + for (int i = 0; i < 5; i++) + { + uint32_t bit = bits[i]; + if ((to & bit) == 0) + continue; + + ExprId cmp; + switch (bit) + { + case 0x10: cmp = il.CompareSignedLessThan(size, a, b); break; + case 0x08: cmp = il.CompareSignedGreaterThan(size, a, b); break; + case 0x04: cmp = il.CompareEqual(size, a, b); break; + case 0x02: cmp = il.CompareUnsignedLessThan(size, a, b); break; + default: cmp = il.CompareUnsignedGreaterThan(size, a, b); break; + } + + cond = first ? cmp : il.Or(size, cond, cmp); + first = false; + } + + return cond; +} + +/* [TO,] rA, (rB | SIMM): trap if the selected comparison of rA holds. */ +static void LiftTrap(LowLevelILFunction& il, Instruction* instruction, size_t size) +{ + uint32_t to; + Operand* aOp; + Operand* bOp; + + switch (instruction->id) + { + // generic forms carry an explicit TO operand + case PPC_ID_TD: + case PPC_ID_TW: + case PPC_ID_TDI: + case PPC_ID_TWI: + to = (uint32_t)instruction->operands[0].uimm; + aOp = &instruction->operands[1]; + bOp = &instruction->operands[2]; + break; + + default: + to = TrapConditionMask(instruction->id); + aOp = &instruction->operands[0]; + bOp = &instruction->operands[1]; + break; + } + + if ((to & 0x1f) == 0) + return; + + if ((to & 0x1f) == 0x1f) + { + il.AddInstruction(il.Trap(0)); + return; + } + + LowLevelILLabel trapLabel, nextLabel; + il.AddInstruction(il.If(TrapCondition(il, to, aOp, bOp, size), trapLabel, nextLabel)); + il.MarkLabel(trapLabel); + il.AddInstruction(il.Trap(0)); + il.MarkLabel(nextLabel); +} + /* returns TRUE - if this IL continues FALSE - if this IL terminates a block */ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, @@ -1114,6 +1282,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(ei0); } + // lwarx establishes a reservation on the accessed address + if(instruction->id == PPC_ID_LWARX) + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_SET_RESERVATION, + {ReservationAddress(il, oper1, oper2, addressSize_l)})); + break; /* @@ -1140,6 +1313,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, */ case PPC_ID_LDX: case PPC_ID_LDUX: + case PPC_ID_LDARX: REQUIRE3OPS ei0 = operToIL(il, oper1, OTI_GPR0_ZERO); // d(rA) or 0 ei0 = il.Load(8, il.Add(8, ei0, operToIL(il, oper2))); // [d(rA) + d(rB)] @@ -1147,8 +1321,41 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(ei0); // if update, rA is set to effective address (d(rA)) - if(instruction->id == PPC_ID_LWZUX && oper1->reg != oper0->reg && oper1->reg != PPC_REG_GPR0) { - ei0 = il.SetRegister(8, oper1->reg, operToIL(il, oper1)); + if(instruction->id == PPC_ID_LDUX && oper1->reg != oper0->reg && oper1->reg != PPC_REG_GPR0) { + ei0 = il.SetRegister(8, oper1->reg, il.Add(8, operToIL(il, oper1), operToIL(il, oper2))); + il.AddInstruction(ei0); + } + + // ldarx establishes a reservation on the accessed address + if(instruction->id == PPC_ID_LDARX) + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_SET_RESERVATION, + {ReservationAddress(il, oper1, oper2, addressSize_l)})); + + break; + + /* load word algebraic: load 4 bytes and sign-extend to the register */ + case PPC_ID_LWA: + REQUIRE2OPS + ei0 = operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l); // d(rA) or 0 + ei0 = il.Load(4, ei0); + ei0 = il.SignExtend(addressSize_l, ei0); + ei0 = il.SetRegister(addressSize_l, oper0->reg, ei0); + il.AddInstruction(ei0); + break; + + case PPC_ID_LWAX: + case PPC_ID_LWAUX: + REQUIRE3OPS + ei0 = operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l); // d(rA) or 0 + ei0 = il.Load(4, il.Add(addressSize_l, ei0, operToIL_a(il, oper2, addressSize_l))); + ei0 = il.SignExtend(addressSize_l, ei0); + ei0 = il.SetRegister(addressSize_l, oper0->reg, ei0); + il.AddInstruction(ei0); + + // if update, rA is set to the effective address (rA + rB) + if(instruction->id == PPC_ID_LWAUX && oper1->reg != oper0->reg && oper1->reg != PPC_REG_GPR0) { + ei0 = il.SetRegister(addressSize_l, oper1->reg, + il.Add(addressSize_l, operToIL_a(il, oper1, addressSize_l), operToIL_a(il, oper2, addressSize_l))); il.AddInstruction(ei0); } @@ -1469,7 +1676,6 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, /* store word indexed [with update] */ case PPC_ID_STWX: - case PPC_ID_STWCX: case PPC_ID_STWUX: /* store(size, addr, val) */ REQUIRE3OPS if (addressSize_l == 8) @@ -1499,6 +1705,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; + case PPC_ID_STWCX: /* store word conditional */ + REQUIRE3OPS + LiftStoreConditional(il, instruction, 4, addressSize_l); + break; + /* store double word [with update] */ case PPC_ID_STD: case PPC_ID_STDU: /* store(size, addr, val) */ @@ -1517,7 +1728,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; - /* store word indexed [with update] */ + /* store double word indexed [with update] */ case PPC_ID_STDX: case PPC_ID_STDUX: /* store(size, addr, val) */ REQUIRE3OPS @@ -1537,6 +1748,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; + case PPC_ID_STDCX: /* store double word conditional */ + REQUIRE3OPS + LiftStoreConditional(il, instruction, 8, addressSize_l); + break; + case PPC_ID_RLWIMIx: REQUIRE5OPS { @@ -1975,6 +2191,78 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, )); break; + case PPC_ID_MULLDx: + REQUIRE3OPS + ei0 = il.Register(8, oper1->reg); + ei0 = il.MultDoublePrecUnsigned(8, ei0, il.Register(8, oper2->reg)); + ei0 = il.LowPart(8, ei0); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + case PPC_ID_MULHDx: + REQUIRE3OPS + ei0 = il.Register(8, oper1->reg); + ei0 = il.MultDoublePrecSigned(8, ei0, il.Register(8, oper2->reg)); + ei0 = il.LowPart(8, il.LogicalShiftRight(16, ei0, il.Const(1, 64))); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + case PPC_ID_MULHDUx: + REQUIRE3OPS + ei0 = il.Register(8, oper1->reg); + ei0 = il.MultDoublePrecUnsigned(8, ei0, il.Register(8, oper2->reg)); + ei0 = il.LowPart(8, il.LogicalShiftRight(16, ei0, il.Const(1, 64))); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + case PPC_ID_DIVDx: + REQUIRE3OPS + ei0 = il.Register(8, oper1->reg); + ei0 = il.DivSigned(8, ei0, il.Register(8, oper2->reg)); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + case PPC_ID_DIVDUx: + REQUIRE3OPS + ei0 = il.Register(8, oper1->reg); + ei0 = il.DivUnsigned(8, ei0, il.Register(8, oper2->reg)); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + case PPC_ID_MODSW: + REQUIRE3OPS + ei0 = il.ModSigned(4, il.Register(4, oper1->reg), il.Register(4, oper2->reg)); + il.AddInstruction(il.SetRegister(4, oper0->reg, ei0)); + break; + + case PPC_ID_MODUW: + REQUIRE3OPS + ei0 = il.ModUnsigned(4, il.Register(4, oper1->reg), il.Register(4, oper2->reg)); + il.AddInstruction(il.SetRegister(4, oper0->reg, ei0)); + break; + + case PPC_ID_MODSD: + REQUIRE3OPS + ei0 = il.ModSigned(8, il.Register(8, oper1->reg), il.Register(8, oper2->reg)); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0)); + break; + + case PPC_ID_MODUD: + REQUIRE3OPS + ei0 = il.ModUnsigned(8, il.Register(8, oper1->reg), il.Register(8, oper2->reg)); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0)); + break; + case PPC_ID_MRx: /* move register */ REQUIRE2OPS ei0 = il.SetRegister(addressSize_l, oper0->reg, operToIL_a(il, oper1, addressSize_l), @@ -1990,8 +2278,47 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(il.Return(il.Unimplemented())); break; + // trap doubleword: trap if the TO-selected comparison of rA holds + case PPC_ID_TD: + case PPC_ID_TDI: + case PPC_ID_TDEQ: + case PPC_ID_TDEQI: + case PPC_ID_TDGT: + case PPC_ID_TDGTI: + case PPC_ID_TDLGT: + case PPC_ID_TDLGTI: + case PPC_ID_TDLLT: + case PPC_ID_TDLLTI: + case PPC_ID_TDLT: + case PPC_ID_TDLTI: + case PPC_ID_TDNE: + case PPC_ID_TDNEI: + case PPC_ID_TDU: + case PPC_ID_TDUI: + LiftTrap(il, instruction, 8); + break; + + // trap word + case PPC_ID_TW: + case PPC_ID_TWI: + case PPC_ID_TWEQ: + case PPC_ID_TWEQI: + case PPC_ID_TWGT: + case PPC_ID_TWGTI: + case PPC_ID_TWGEI: + case PPC_ID_TWLEI: + case PPC_ID_TWLLEI: + case PPC_ID_TWLGT: + case PPC_ID_TWLGTI: + case PPC_ID_TWLLT: + case PPC_ID_TWLLTI: + case PPC_ID_TWLT: + case PPC_ID_TWLTI: + case PPC_ID_TWNE: + case PPC_ID_TWNEI: case PPC_ID_TWU: - il.AddInstruction(il.Trap(0)); + case PPC_ID_TWUI: + LiftTrap(il, instruction, 4); break; // ===================================== diff --git a/arch/powerpc/il.h b/arch/powerpc/il.h index 1e8f67c21d..d747514427 100644 --- a/arch/powerpc/il.h +++ b/arch/powerpc/il.h @@ -211,8 +211,9 @@ typedef enum enum PPCIntrinsic : uint32_t { - PPC_INTRIN_CNTLZW, PPC_INTRIN_FRSP, + PPC_INTRIN_SET_RESERVATION, // lwarx/ldarx: establish a reservation + PPC_INTRIN_CHECK_RESERVATION, // stwcx./stdcx.: is the reservation still held? PPC_INTRIN_END, PPC_PS_INTRIN_QUANTIZE, PPC_PS_INTRIN_DEQUANTIZE, From b0096c466246bd38c606da8b7e0159a7ca79beb3 Mon Sep 17 00:00:00 2001 From: Marvis Date: Sun, 2 Aug 2026 14:30:51 +0200 Subject: [PATCH 4/8] Fix PowerPC load/store update forms and 64-bit sizes in lifting - stdu/ldu never updated rA (guards checked the wrong instruction ID) - lbzux/lhzux/lhaux/lwzux updates were no-ops (rA = rA instead of rA + rB) - lbzu passed the address size into operToIL's options parameter - ld/ldu/ldx/ldux/ldarx/stdx/stdux read registers at 4 bytes, truncating addresses (and stdx's stored value) to 32 bits on ppc64 - load_float used hardcoded 32-bit address math and update writes - trap lifting now emits a nop for the TO=0 never-trap form and guards operand counts --- arch/powerpc/il.cpp | 78 +++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/arch/powerpc/il.cpp b/arch/powerpc/il.cpp index c1839019da..24ef460831 100644 --- a/arch/powerpc/il.cpp +++ b/arch/powerpc/il.cpp @@ -462,11 +462,11 @@ static void load_float(LowLevelILFunction& il, Operand* operand1, /* register that gets read/written */ Operand* operand2, /* location the read/write occurs */ Operand* operand3=0, - bool update=false + bool update=false, + size_t addrsz=4 ) { ExprId tmp = BN_INVALID_EXPR; - const int addrsz = 4; // assume single if (!load_sz) load_sz = 4; @@ -481,21 +481,21 @@ static void load_float(LowLevelILFunction& il, else { tmp = il.Add(addrsz, il.Register(addrsz, operand2->mem.reg), il.Const(addrsz, operand2->mem.offset)); - } + } } else if(operand2->cls == PPC_OP_REG_RA) { if ((operand3 != 0) && (operand3->cls == PPC_OP_REG_RB)) { - tmp = il.Add(4, il.Register(addrsz, operand2->reg), il.Register(addrsz, operand3->reg)); + tmp = il.Add(addrsz, il.Register(addrsz, operand2->reg), il.Register(addrsz, operand3->reg)); } } il.AddInstruction(il.SetRegister(load_sz, operand1->reg, il.FloatConvert(load_sz, il.Operand(1, il.Load(load_sz, tmp))))); - + if (update == true) { - tmp = il.SetRegister(4, operand2->reg, tmp); + tmp = il.SetRegister(addrsz, operand2->reg, tmp); il.AddInstruction(tmp); } } @@ -633,6 +633,8 @@ static void LiftTrap(LowLevelILFunction& il, Instruction* instruction, size_t si Operand* aOp; Operand* bOp; + size_t neededOps; + switch (instruction->id) { // generic forms carry an explicit TO operand @@ -640,20 +642,32 @@ static void LiftTrap(LowLevelILFunction& il, Instruction* instruction, size_t si case PPC_ID_TW: case PPC_ID_TDI: case PPC_ID_TWI: + if (instruction->numOperands < 3) + { + il.AddInstruction(il.Unimplemented()); + return; + } + to = (uint32_t)instruction->operands[0].uimm; aOp = &instruction->operands[1]; bOp = &instruction->operands[2]; + neededOps = 3; break; default: to = TrapConditionMask(instruction->id); aOp = &instruction->operands[0]; bOp = &instruction->operands[1]; + neededOps = 2; break; } + // TO=0 never traps; the instruction is an architectural no-op if ((to & 0x1f) == 0) + { + il.AddInstruction(il.Nop()); return; + } if ((to & 0x1f) == 0x1f) { @@ -661,6 +675,13 @@ static void LiftTrap(LowLevelILFunction& il, Instruction* instruction, size_t si return; } + // only the conditional forms read their comparison operands + if (instruction->numOperands < neededOps) + { + il.AddInstruction(il.Unimplemented()); + return; + } + LowLevelILLabel trapLabel, nextLabel; il.AddInstruction(il.If(TrapCondition(il, to, aOp, bOp, size), trapLabel, nextLabel)); il.MarkLabel(trapLabel); @@ -1142,7 +1163,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, // if update, rA is set to effective address (d(rA)) if(instruction->id == PPC_ID_LBZU) { - ei0 = il.SetRegister(addressSize_l, oper1->mem.reg, operToIL(il, oper1, addressSize_l)); + ei0 = il.SetRegister(addressSize_l, oper1->mem.reg, operToIL_a(il, oper1, addressSize_l)); il.AddInstruction(ei0); } @@ -1161,10 +1182,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, ei0 = il.SetRegister(addressSize_l, oper0->reg, ei0); // rD = [d(rA)] il.AddInstruction(ei0); - // if update, rA is set to effective address (d(rA)) + // if update, rA is set to effective address (rA + rB) if (instruction->id == PPC_ID_LBZUX && oper1->reg != oper0->reg && oper1->reg != PPC_REG_GPR0) { - ei0 = il.SetRegister(addressSize_l, oper1->reg, operToIL_a(il, oper1, addressSize_l)); + ei0 = il.SetRegister(addressSize_l, oper1->reg, + il.Add(addressSize_l, operToIL_a(il, oper1, addressSize_l), operToIL_a(il, oper2, addressSize_l))); il.AddInstruction(ei0); } @@ -1221,9 +1243,10 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, ei0 = il.SetRegister(addressSize_l, oper0->reg, ei0); // rD = [d(rA)] il.AddInstruction(ei0); - // if update, rA is set to effective address (d(rA)) + // if update, rA is set to effective address (rA + rB) if((instruction->id == PPC_ID_LHZUX || instruction->id == PPC_ID_LHAUX) && oper1->reg != oper0->reg && oper1->reg != PPC_REG_GPR0) { - ei0 = il.SetRegister(addressSize_l, oper1->reg, operToIL_a(il, oper1, addressSize_l)); + ei0 = il.SetRegister(addressSize_l, oper1->reg, + il.Add(addressSize_l, operToIL_a(il, oper1, addressSize_l), operToIL_a(il, oper2, addressSize_l))); il.AddInstruction(ei0); } @@ -1276,9 +1299,10 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, ei0 = il.SetRegister(addressSize_l, oper0->reg, ei0); // rD = [d(rA)] il.AddInstruction(ei0); - // if update, rA is set to effective address (d(rA)) + // if update, rA is set to effective address (rA + rB) if(instruction->id == PPC_ID_LWZUX && oper1->reg != oper0->reg && oper1->reg != PPC_REG_GPR0) { - ei0 = il.SetRegister(addressSize_l, oper1->reg, operToIL_a(il, oper1, addressSize_l)); + ei0 = il.SetRegister(addressSize_l, oper1->reg, + il.Add(addressSize_l, operToIL_a(il, oper1, addressSize_l), operToIL_a(il, oper2, addressSize_l))); il.AddInstruction(ei0); } @@ -1295,14 +1319,14 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, case PPC_ID_LD: case PPC_ID_LDU: REQUIRE2OPS - ei0 = operToIL(il, oper1, OTI_GPR0_ZERO); // d(rA) or 0 + ei0 = operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, 8); // d(rA) or 0 ei0 = il.Load(8, ei0); // [d(rA)] ei0 = il.SetRegister(8, oper0->reg, ei0); // rD = [d(rA)] il.AddInstruction(ei0); // if update, rA is set to effective address (d(rA)) - if(instruction->id == PPC_ID_LWZU) { - ei0 = il.SetRegister(8, oper1->mem.reg, operToIL(il, oper1)); + if(instruction->id == PPC_ID_LDU) { + ei0 = il.SetRegister(8, oper1->mem.reg, operToIL_a(il, oper1, 8)); il.AddInstruction(ei0); } @@ -1315,14 +1339,14 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, case PPC_ID_LDUX: case PPC_ID_LDARX: REQUIRE3OPS - ei0 = operToIL(il, oper1, OTI_GPR0_ZERO); // d(rA) or 0 - ei0 = il.Load(8, il.Add(8, ei0, operToIL(il, oper2))); // [d(rA) + d(rB)] + ei0 = operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, 8); // d(rA) or 0 + ei0 = il.Load(8, il.Add(8, ei0, operToIL_a(il, oper2, 8))); // [d(rA) + d(rB)] ei0 = il.SetRegister(8, oper0->reg, ei0); // rD = [d(rA)] il.AddInstruction(ei0); // if update, rA is set to effective address (d(rA)) if(instruction->id == PPC_ID_LDUX && oper1->reg != oper0->reg && oper1->reg != PPC_REG_GPR0) { - ei0 = il.SetRegister(8, oper1->reg, il.Add(8, operToIL(il, oper1), operToIL(il, oper2))); + ei0 = il.SetRegister(8, oper1->reg, il.Add(8, operToIL_a(il, oper1, 8), operToIL_a(il, oper2, 8))); il.AddInstruction(ei0); } @@ -1721,7 +1745,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(ei0); // if update, then rA gets updated address - if(instruction->id == PPC_ID_STWU) { + if(instruction->id == PPC_ID_STDU) { ei0 = il.SetRegister(8, oper1->mem.reg, operToIL_a(il, oper1, 8)); il.AddInstruction(ei0); } @@ -1733,8 +1757,8 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, case PPC_ID_STDUX: /* store(size, addr, val) */ REQUIRE3OPS ei0 = il.Store(8, - il.Add(8, operToIL(il, oper1, OTI_GPR0_ZERO), operToIL_a(il, oper2, addressSize_l)), - operToIL(il, oper0) + il.Add(8, operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, 8), operToIL_a(il, oper2, 8)), + operToIL_a(il, oper0, 8) ); il.AddInstruction(ei0); @@ -2411,7 +2435,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, // il.AddInstruction(ei1); // alternatively, do it the way arm64 does it - load_float(il, 4, oper0, oper1); + load_float(il, 4, oper0, oper1, 0, false, addressSize_l); break; case PPC_ID_LFSX: @@ -2424,17 +2448,17 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, // // alternatively, do it the way arm64 does it // il.AddInstruction(ei0); - load_float(il, 4, oper0, oper1, oper2); + load_float(il, 4, oper0, oper1, oper2, false, addressSize_l); break; case PPC_ID_LFSU: REQUIRE2OPS - load_float(il, 4, oper0, oper1, 0, true); + load_float(il, 4, oper0, oper1, 0, true, addressSize_l); break; case PPC_ID_LFSUX: REQUIRE3OPS - load_float(il, 4, oper0, oper1, oper2, true); + load_float(il, 4, oper0, oper1, oper2, true, addressSize_l); break; case PPC_ID_LFD: @@ -2445,7 +2469,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, // il.AddInstruction(ei0); // same as lfs - load_float(il, 8, oper0, oper1); + load_float(il, 8, oper0, oper1, 0, false, addressSize_l); break; case PPC_ID_FMULx: From 4030ab97818133e388e0e9d6da682dcf96f34ecb Mon Sep 17 00:00:00 2001 From: Marvis Date: Sun, 2 Aug 2026 14:40:48 +0200 Subject: [PATCH 5/8] Lift more PowerPC instructions - trap: the canonical unconditional trap encoding (tw 31,0,0) now lifts to a trap instead of unimplemented - lnia/addpcis: PC materialization lifts to the actual address constant - lbarx/lharx and stbcx./sthcx.: byte/halfword reservation forms wired into the existing load-reserve/store-conditional modeling - lfdx/lfdu/lfdux and stfsu/stfsux/stfdx/stfdu/stfdux: double/update FP load and store forms, matching the existing lfs/stfs conventions - ldbrx/stdbrx: byte-reversed doubleword accesses (ByteReverseRegister generalized to 8-byte operations) - rfid/hrfid/rfci/rfdi/rfmci/rfebb: lifted as returns like rfi so blocks terminate properly in kernel/firmware code - xnop lifts to a nop Also fixes in this area: load_float compared the base register against 0 instead of PPC_REG_GPR0 (lfs/lfd with rA=0 read r0 instead of using 0), its X-form path produced invalid IL for rA=0, and stfs/stfsx/stfd computed addresses with 4-byte math on ppc64. --- arch/powerpc/il.cpp | 161 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 137 insertions(+), 24 deletions(-) diff --git a/arch/powerpc/il.cpp b/arch/powerpc/il.cpp index 24ef460831..ccc90c3cdb 100644 --- a/arch/powerpc/il.cpp +++ b/arch/powerpc/il.cpp @@ -402,29 +402,30 @@ static bool LiftBranches(Architecture* arch, LowLevelILFunction &il, const Instr static ExprId ByteReverseRegister(LowLevelILFunction &il, uint32_t reg, size_t size) { ExprId swap = BN_INVALID_EXPR; + size_t opsz = (size > 4) ? 8 : 4; for (size_t srcIndex = 0; srcIndex < size; srcIndex++) { - ExprId extracted = il.Register(4, reg); + ExprId extracted = il.Register(opsz, reg); size_t dstIndex = size - srcIndex - 1; if (dstIndex > srcIndex) { - ExprId mask = il.Const(4, 0xffull << (srcIndex * 8)); - extracted = il.And(4, extracted, mask); - extracted = il.ShiftLeft(4, extracted, il.Const(4, (dstIndex - srcIndex) * 8)); + ExprId mask = il.Const(opsz, 0xffull << (srcIndex * 8)); + extracted = il.And(opsz, extracted, mask); + extracted = il.ShiftLeft(opsz, extracted, il.Const(opsz, (dstIndex - srcIndex) * 8)); } else if (srcIndex > dstIndex) { - ExprId mask = il.Const(4, 0xffull << (dstIndex * 8)); - extracted = il.LogicalShiftRight(4, extracted, il.Const(4, (srcIndex - dstIndex) * 8)); - extracted = il.And(4, extracted, mask); + ExprId mask = il.Const(opsz, 0xffull << (dstIndex * 8)); + extracted = il.LogicalShiftRight(opsz, extracted, il.Const(opsz, (srcIndex - dstIndex) * 8)); + extracted = il.And(opsz, extracted, mask); } if (swap == BN_INVALID_EXPR) swap = extracted; else - swap = il.Or(4, swap, extracted); + swap = il.Or(opsz, swap, extracted); } return swap; @@ -474,7 +475,7 @@ static void load_float(LowLevelILFunction& il, // operand1.reg = [operand2.reg + operand2.imm] if (operand2->cls == PPC_OP_MEM_RA) { - if (operand2->mem.reg == 0) + if (operand2->mem.reg == PPC_REG_GPR0) { tmp = il.Const(addrsz, operand2->mem.offset); } @@ -483,12 +484,10 @@ static void load_float(LowLevelILFunction& il, tmp = il.Add(addrsz, il.Register(addrsz, operand2->mem.reg), il.Const(addrsz, operand2->mem.offset)); } } - else if(operand2->cls == PPC_OP_REG_RA) + else if ((operand3 != 0) && (operand3->cls == PPC_OP_REG_RB)) { - if ((operand3 != 0) && (operand3->cls == PPC_OP_REG_RB)) - { - tmp = il.Add(addrsz, il.Register(addrsz, operand2->reg), il.Register(addrsz, operand3->reg)); - } + // X-form: (rA|0) + rB; rA=0 is decoded as a UIMM 0 operand + tmp = il.Add(addrsz, operToIL_a(il, operand2, addrsz), operToIL_a(il, operand3, addrsz)); } il.AddInstruction(il.SetRegister(load_sz, operand1->reg, il.FloatConvert(load_sz, il.Operand(1, il.Load(load_sz, tmp))))); @@ -567,7 +566,8 @@ static uint32_t TrapConditionMask(uint32_t id) case PPC_ID_TDNE: case PPC_ID_TDNEI: case PPC_ID_TWNE: case PPC_ID_TWNEI: return 0x18; // a != b case PPC_ID_TDU: case PPC_ID_TDUI: - case PPC_ID_TWU: case PPC_ID_TWUI: return 0x1f; // unconditional + case PPC_ID_TWU: case PPC_ID_TWUI: + case PPC_ID_TRAP: return 0x1f; // unconditional case PPC_ID_TWGEI: return 0x0c; // a >=s b case PPC_ID_TWLEI: return 0x14; // a <=s b case PPC_ID_TWLLEI: return 0x06; // a <=u b @@ -1175,6 +1175,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, */ case PPC_ID_LBZX: case PPC_ID_LBZUX: + case PPC_ID_LBARX: REQUIRE3OPS ei0 = operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l); // d(rA) or 0 ei0 = il.Load(1, il.Add(addressSize_l, ei0, operToIL_a(il, oper2, addressSize_l))); // [d(rA) + d(rB)] @@ -1190,6 +1191,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(ei0); } + // lbarx establishes a reservation on the accessed address + if (instruction->id == PPC_ID_LBARX) + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_SET_RESERVATION, + {ReservationAddress(il, oper1, oper2, addressSize_l)})); + break; /* @@ -1233,13 +1239,14 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, case PPC_ID_LHZUX: case PPC_ID_LHAX: case PPC_ID_LHAUX: + case PPC_ID_LHARX: REQUIRE3OPS ei0 = operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l); // d(rA) or 0 ei0 = il.Load(2, il.Add(addressSize_l, ei0, operToIL_a(il, oper2, addressSize_l))); // [d(rA) + d(rB)] - if(instruction->id == PPC_ID_LHZX || instruction->id == PPC_ID_LHZUX) - ei0 = il.ZeroExtend(addressSize_l, ei0); - else + if(instruction->id == PPC_ID_LHAX || instruction->id == PPC_ID_LHAUX) ei0 = il.SignExtend(addressSize_l, ei0); + else + ei0 = il.ZeroExtend(addressSize_l, ei0); ei0 = il.SetRegister(addressSize_l, oper0->reg, ei0); // rD = [d(rA)] il.AddInstruction(ei0); @@ -1250,6 +1257,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(ei0); } + // lharx establishes a reservation on the accessed address + if (instruction->id == PPC_ID_LHARX) + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_SET_RESERVATION, + {ReservationAddress(il, oper1, oper2, addressSize_l)})); + break; /* @@ -1395,6 +1407,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, ByteReversedLoad(il, instruction, 4, addressSize_l); break; + case PPC_ID_LDBRX: + REQUIRE3OPS + ByteReversedLoad(il, instruction, 8, addressSize_l); + break; + case PPC_ID_STHBRX: REQUIRE3OPS ByteReversedStore(il, instruction, 2, addressSize_l); @@ -1405,6 +1422,11 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, ByteReversedStore(il, instruction, 4, addressSize_l); break; + case PPC_ID_STDBRX: + REQUIRE3OPS + ByteReversedStore(il, instruction, 8, addressSize_l); + break; + case PPC_ID_MFCTR: // move from ctr REQUIRE1OP il.AddInstruction(il.SetRegister(addressSize_l, oper0->reg, il.Register(addressSize_l, PPC_REG_CTR))); @@ -1434,6 +1456,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; case PPC_ID_NOP: + case PPC_ID_XNOP: il.AddInstruction(il.Nop()); break; @@ -1729,6 +1752,16 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; + case PPC_ID_STBCX: /* store byte conditional */ + REQUIRE3OPS + LiftStoreConditional(il, instruction, 1, addressSize_l); + break; + + case PPC_ID_STHCX: /* store half word conditional */ + REQUIRE3OPS + LiftStoreConditional(il, instruction, 2, addressSize_l); + break; + case PPC_ID_STWCX: /* store word conditional */ REQUIRE3OPS LiftStoreConditional(il, instruction, 4, addressSize_l); @@ -2287,6 +2320,21 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(il.SetRegister(8, oper0->reg, ei0)); break; + /* rD = address of the next instruction */ + case PPC_ID_LNIA: + REQUIRE1OP + il.AddInstruction(il.SetRegister(addressSize_l, oper0->reg, + il.ConstPointer(addressSize_l, addr + 4))); + break; + + /* rD = address of the next instruction + (signed d << 16) */ + case PPC_ID_ADDPCIS: + REQUIRE2OPS + il.AddInstruction(il.SetRegister(addressSize_l, oper0->reg, + il.ConstPointer(addressSize_l, + addr + 4 + (int64_t)(int32_t)((uint32_t)oper1->uimm << 16)))); + break; + case PPC_ID_MRx: /* move register */ REQUIRE2OPS ei0 = il.SetRegister(addressSize_l, oper0->reg, operToIL_a(il, oper1, addressSize_l), @@ -2299,6 +2347,12 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; case PPC_ID_RFI: + case PPC_ID_RFID: + case PPC_ID_HRFID: + case PPC_ID_RFCI: + case PPC_ID_RFDI: + case PPC_ID_RFMCI: + case PPC_ID_RFEBB: il.AddInstruction(il.Return(il.Unimplemented())); break; @@ -2342,6 +2396,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, case PPC_ID_TWNEI: case PPC_ID_TWU: case PPC_ID_TWUI: + case PPC_ID_TRAP: LiftTrap(il, instruction, 4); break; @@ -2402,28 +2457,71 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; case PPC_ID_STFS: + case PPC_ID_STFSU: REQUIRE2OPS ei0 = il.FloatConvert(4, operToIL(il, oper0)); - ei0 = il.Store(4, operToIL(il, oper1), ei0); - // ei0 = il.FloatConvert(4, ei0); + ei0 = il.Store(4, operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), ei0); il.AddInstruction(ei0); + + // if update, rA is set to the effective address + if (instruction->id == PPC_ID_STFSU) { + ei0 = il.SetRegister(addressSize_l, oper1->mem.reg, operToIL_a(il, oper1, addressSize_l)); + il.AddInstruction(ei0); + } + break; case PPC_ID_STFSX: + case PPC_ID_STFSUX: REQUIRE3OPS ei0 = il.FloatConvert(4, operToIL(il, oper0)); - ei1 = il.Add(4, operToIL(il, oper1), operToIL(il, oper2)); + ei1 = il.Add(addressSize_l, + operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), + operToIL_a(il, oper2, addressSize_l)); ei0 = il.Store(4, ei1, ei0); - // ei0 = il.FloatConvert(4, ei0); il.AddInstruction(ei0); + + // if update, rA is set to the effective address (rA + rB) + if (instruction->id == PPC_ID_STFSUX) { + ei0 = il.SetRegister(addressSize_l, oper1->reg, + il.Add(addressSize_l, operToIL_a(il, oper1, addressSize_l), operToIL_a(il, oper2, addressSize_l))); + il.AddInstruction(ei0); + } + break; case PPC_ID_STFD: + case PPC_ID_STFDU: REQUIRE2OPS - ei0 = il.Store(8, operToIL(il, oper1), + ei0 = il.Store(8, operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), il.FloatConvert(8, operToIL_a(il, oper0, 8))); - // ei0 = il.FloatConvert(8, ei0); il.AddInstruction(ei0); + + // if update, rA is set to the effective address + if (instruction->id == PPC_ID_STFDU) { + ei0 = il.SetRegister(addressSize_l, oper1->mem.reg, operToIL_a(il, oper1, addressSize_l)); + il.AddInstruction(ei0); + } + + break; + + case PPC_ID_STFDX: + case PPC_ID_STFDUX: + REQUIRE3OPS + ei0 = il.Store(8, + il.Add(addressSize_l, + operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), + operToIL_a(il, oper2, addressSize_l)), + il.FloatConvert(8, operToIL_a(il, oper0, 8))); + il.AddInstruction(ei0); + + // if update, rA is set to the effective address (rA + rB) + if (instruction->id == PPC_ID_STFDUX) { + ei0 = il.SetRegister(addressSize_l, oper1->reg, + il.Add(addressSize_l, operToIL_a(il, oper1, addressSize_l), operToIL_a(il, oper2, addressSize_l))); + il.AddInstruction(ei0); + } + break; case PPC_ID_LFS: @@ -2472,6 +2570,21 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, load_float(il, 8, oper0, oper1, 0, false, addressSize_l); break; + case PPC_ID_LFDX: + REQUIRE3OPS + load_float(il, 8, oper0, oper1, oper2, false, addressSize_l); + break; + + case PPC_ID_LFDU: + REQUIRE2OPS + load_float(il, 8, oper0, oper1, 0, true, addressSize_l); + break; + + case PPC_ID_LFDUX: + REQUIRE3OPS + load_float(il, 8, oper0, oper1, oper2, true, addressSize_l); + break; + case PPC_ID_FMULx: REQUIRE3OPS ei0 = il.MultDoublePrecSigned(8, operToIL_a(il, oper1, 8), From c354a667274df54b388532e8cf247366956762f8 Mon Sep 17 00:00:00 2001 From: Marvis Date: Sun, 2 Aug 2026 14:53:50 +0200 Subject: [PATCH 6/8] Lift PowerPC scalar FP tail, extended divides, and string ops - fsqrt/fsqrts -> FloatSqrt; frin/friz/frip/frim -> RoundToInt/ FloatTrunc/Ceil/Floor - fctiw[u][z]/fctid[u][z] -> FloatToInt and fcfid[u][s] -> IntToFloat (LLIL has no unsigned or rounding-mode-aware conversions, so u/z variants map to the signed conversion) - fsel lifted as a compare-and-select, fcpsgn as sign/magnitude bit ops - lfiwax/lfiwzx/stfiwx integer-word FPR accesses - extswsli, divwe/divweu (widened 64-bit divide) and divde/divdeu (double-precision divide) - lswi/stswi unrolled into word/byte loads and stores at lift time - setb from the CR field's LT/GT flags Replaces the previous fctiwz lifting, which never converted to an integer (FloatTrunc is a float->float rounding), read the double at 4 bytes, and left the result in the wrong half of the register due to an IBM bit-numbering mix-up. --- arch/powerpc/il.cpp | 304 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 291 insertions(+), 13 deletions(-) diff --git a/arch/powerpc/il.cpp b/arch/powerpc/il.cpp index ccc90c3cdb..18be586ac9 100644 --- a/arch/powerpc/il.cpp +++ b/arch/powerpc/il.cpp @@ -1142,6 +1142,91 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; + /* load string word immediate: NB bytes from (rA|0), packed big-endian + into successive registers (wrapping r31 -> r0) */ + case PPC_ID_LSWI: + { + REQUIRE3OPS + uint32_t nb = (uint32_t)oper2->uimm; + if (nb == 0) + nb = 32; + + uint32_t dIndex = oper0->reg - PPC_REG_GPR0; + uint32_t offset = 0; + for (uint32_t r = 0; offset < nb; r++) + { + uint32_t reg = PPC_REG_GPR0 + ((dIndex + r) & 31); + uint32_t remaining = nb - offset; + + if (remaining >= 4) + { + il.AddInstruction(il.SetRegister(4, reg, il.Load(4, il.Add(addressSize_l, + operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), + il.Const(addressSize_l, offset))))); + offset += 4; + } + else + { + // final partial register: bytes are left-justified, the rest zeroed + ei0 = BN_INVALID_EXPR; + for (uint32_t j = 0; j < remaining; j++) + { + ei1 = il.ShiftLeft(4, + il.ZeroExtend(4, il.Load(1, il.Add(addressSize_l, + operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), + il.Const(addressSize_l, offset + j)))), + il.Const(1, (3 - j) * 8)); + ei0 = (j == 0) ? ei1 : il.Or(4, ei0, ei1); + } + il.AddInstruction(il.SetRegister(4, reg, ei0)); + offset = nb; + } + } + + break; + } + + /* store string word immediate: NB bytes to (rA|0) from successive + registers (wrapping r31 -> r0), most-significant byte first */ + case PPC_ID_STSWI: + { + REQUIRE3OPS + uint32_t nb = (uint32_t)oper2->uimm; + if (nb == 0) + nb = 32; + + uint32_t sIndex = oper0->reg - PPC_REG_GPR0; + uint32_t offset = 0; + for (uint32_t r = 0; offset < nb; r++) + { + uint32_t reg = PPC_REG_GPR0 + ((sIndex + r) & 31); + uint32_t remaining = nb - offset; + + if (remaining >= 4) + { + il.AddInstruction(il.Store(4, il.Add(addressSize_l, + operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), + il.Const(addressSize_l, offset)), + il.Register(4, reg))); + offset += 4; + } + else + { + // final partial register: leftmost bytes stored first + for (uint32_t j = 0; j < remaining; j++) + { + il.AddInstruction(il.Store(1, il.Add(addressSize_l, + operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), + il.Const(addressSize_l, offset + j)), + il.LowPart(1, il.LogicalShiftRight(4, il.Register(4, reg), il.Const(1, (3 - j) * 8))))); + } + offset = nb; + } + } + + break; + } + /* load byte and zero extend [and update] */ @@ -2296,6 +2381,70 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, )); break; + /* divide extended: rD = (rA << 32) / rB */ + case PPC_ID_DIVWEx: + REQUIRE3OPS + ei0 = il.DivSigned(8, + il.ShiftLeft(8, il.SignExtend(8, il.Register(4, oper1->reg)), il.Const(1, 32)), + il.SignExtend(8, il.Register(4, oper2->reg))); + il.AddInstruction(il.SetRegister(4, oper0->reg, il.LowPart(4, ei0), + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + case PPC_ID_DIVWEUx: + REQUIRE3OPS + ei0 = il.DivUnsigned(8, + il.ShiftLeft(8, il.ZeroExtend(8, il.Register(4, oper1->reg)), il.Const(1, 32)), + il.ZeroExtend(8, il.Register(4, oper2->reg))); + il.AddInstruction(il.SetRegister(4, oper0->reg, il.LowPart(4, ei0), + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + /* divide extended doubleword: rD = (rA << 64) / rB */ + case PPC_ID_DIVDEx: + REQUIRE3OPS + ei0 = il.DivDoublePrecSigned(8, + il.ShiftLeft(16, il.SignExtend(16, il.Register(8, oper1->reg)), il.Const(1, 64)), + il.Register(8, oper2->reg)); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + case PPC_ID_DIVDEUx: + REQUIRE3OPS + ei0 = il.DivDoublePrecUnsigned(8, + il.ShiftLeft(16, il.ZeroExtend(16, il.Register(8, oper1->reg)), il.Const(1, 64)), + il.Register(8, oper2->reg)); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + /* rA = (rS sign-extended from 32 bits) << SH */ + case PPC_ID_EXTSWSLIx: + REQUIRE3OPS + ei0 = il.SignExtend(8, il.Register(4, oper1->reg)); + ei0 = il.ShiftLeft(8, ei0, il.Const(1, oper2->uimm)); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0, + instruction->flags.rc ? IL_FLAGWRITE_CR0_S : 0 + )); + break; + + /* rD = -1, 1, or 0 from the LT/GT bits of the given CR field */ + case PPC_ID_SETB: + { + REQUIRE2OPS + uint32_t crf = oper1->reg - PPC_REG_CRF0; + ei0 = il.Sub(addressSize_l, + il.BoolToInt(addressSize_l, il.Flag(4*crf + IL_FLAG_GT)), + il.BoolToInt(addressSize_l, il.Flag(4*crf + IL_FLAG_LT))); + il.AddInstruction(il.SetRegister(addressSize_l, oper0->reg, ei0)); + break; + } + case PPC_ID_MODSW: REQUIRE3OPS ei0 = il.ModSigned(4, il.Register(4, oper1->reg), il.Register(4, oper2->reg)); @@ -2456,6 +2605,124 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(ei0); break; + case PPC_ID_FSQRTx: + REQUIRE2OPS + ei0 = il.FloatSqrt(8, operToIL_a(il, oper1, 8)); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + case PPC_ID_FSQRTSx: + REQUIRE2OPS + ei0 = il.FloatSqrt(4, operToIL(il, oper1)); + ei0 = il.SetRegister(4, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + /* round to double-precision integer: frin to nearest, friz toward zero, + frip toward +inf, frim toward -inf */ + case PPC_ID_FRINx: + REQUIRE2OPS + ei0 = il.RoundToInt(8, operToIL_a(il, oper1, 8)); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + case PPC_ID_FRIZx: + REQUIRE2OPS + ei0 = il.FloatTrunc(8, operToIL_a(il, oper1, 8)); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + case PPC_ID_FRIPx: + REQUIRE2OPS + ei0 = il.Ceil(8, operToIL_a(il, oper1, 8)); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + case PPC_ID_FRIMx: + REQUIRE2OPS + ei0 = il.Floor(8, operToIL_a(il, oper1, 8)); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + /* convert to integer word: the result occupies the low 32 bits of frD. + LLIL has no unsigned or rounding-mode-aware conversions, so the u/z + variants all map to the same signed conversion. */ + case PPC_ID_FCTIWx: + case PPC_ID_FCTIWZx: + REQUIRE2OPS + ei0 = il.SignExtend(8, il.FloatToInt(4, operToIL_a(il, oper1, 8))); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + case PPC_ID_FCTIWUx: + case PPC_ID_FCTIWUZx: + REQUIRE2OPS + ei0 = il.ZeroExtend(8, il.FloatToInt(4, operToIL_a(il, oper1, 8))); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + /* convert to integer doubleword */ + case PPC_ID_FCTIDx: + case PPC_ID_FCTIDZx: + case PPC_ID_FCTIDUx: + case PPC_ID_FCTIDUZx: + REQUIRE2OPS + ei0 = il.FloatToInt(8, operToIL_a(il, oper1, 8)); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + /* convert integer doubleword to floating point */ + case PPC_ID_FCFIDx: + case PPC_ID_FCFIDUx: + REQUIRE2OPS + ei0 = il.IntToFloat(8, operToIL_a(il, oper1, 8)); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + case PPC_ID_FCFIDSx: + case PPC_ID_FCFIDUSx: + REQUIRE2OPS + ei0 = il.IntToFloat(4, operToIL_a(il, oper1, 8)); + ei0 = il.SetRegister(4, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + /* frD = sign of frA with magnitude of frB */ + case PPC_ID_FCPSGNx: + REQUIRE3OPS + ei0 = il.Or(8, + il.And(8, operToIL_a(il, oper1, 8), il.Const(8, 0x8000000000000000ULL)), + il.And(8, operToIL_a(il, oper2, 8), il.Const(8, 0x7fffffffffffffffULL))); + ei0 = il.SetRegister(8, oper0->reg, ei0, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); + il.AddInstruction(ei0); + break; + + /* frD = (frA >= 0.0) ? frC : frB */ + case PPC_ID_FSELx: + { + REQUIRE4OPS + LowLevelILLabel selTrue, selFalse, selDone; + il.AddInstruction(il.If( + il.FloatCompareGreaterEqual(8, operToIL_a(il, oper1, 8), il.FloatConstDouble(0.0)), + selTrue, selFalse)); + il.MarkLabel(selTrue); + il.AddInstruction(il.SetRegister(8, oper0->reg, operToIL_a(il, oper2, 8))); + il.AddInstruction(il.Goto(selDone)); + il.MarkLabel(selFalse); + il.AddInstruction(il.SetRegister(8, oper0->reg, operToIL_a(il, oper3, 8))); + il.MarkLabel(selDone); + break; + } + case PPC_ID_STFS: case PPC_ID_STFSU: REQUIRE2OPS @@ -2524,6 +2791,16 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; + /* store the low integer word of frS */ + case PPC_ID_STFIWX: + REQUIRE3OPS + il.AddInstruction(il.Store(4, + il.Add(addressSize_l, + operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), + operToIL_a(il, oper2, addressSize_l)), + il.LowPart(4, operToIL_a(il, oper0, 8)))); + break; + case PPC_ID_LFS: REQUIRE2OPS // ei0 = operToIL(il, oper1); // d(rA) or 0 @@ -2585,6 +2862,20 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, load_float(il, 8, oper0, oper1, oper2, true, addressSize_l); break; + /* load integer word into frD, sign/zero extended */ + case PPC_ID_LFIWAX: + case PPC_ID_LFIWZX: + REQUIRE3OPS + ei0 = il.Load(4, il.Add(addressSize_l, + operToIL(il, oper1, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), + operToIL_a(il, oper2, addressSize_l))); + if (instruction->id == PPC_ID_LFIWAX) + ei0 = il.SignExtend(8, ei0); + else + ei0 = il.ZeroExtend(8, ei0); + il.AddInstruction(il.SetRegister(8, oper0->reg, ei0)); + break; + case PPC_ID_FMULx: REQUIRE3OPS ei0 = il.MultDoublePrecSigned(8, operToIL_a(il, oper1, 8), @@ -2649,19 +2940,6 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(ei1); break; - // this is a weird one, its described as round a float to an int towards 0, then set - // bits 32-63 of a double reg to that result, ignoring the lower 32 bits 0-31. - // TODO: needs further testing to verify that this is functional, and verify that the - // method used was correct, as well as the registers afffected, like FPSCR. - case PPC_ID_FCTIWZx: - REQUIRE2OPS - ei0 = il.FloatTrunc(RZF, operToIL(il, oper1)); - ei1 = il.Const(4, 32); - ei2 = il.ShiftLeft(8, ei0, ei1); - ei0 = il.SetRegister(8, oper0->reg, ei2, (instruction->flags.rc) ? IL_FLAGWRITE_CR0_F : 0); - il.AddInstruction(ei0); - break; - case PPC_ID_FNEGx: REQUIRE2OPS ei0 = il.FloatNeg(4, operToIL(il, oper1)); From 3d432b10e15633f15e0d22e00fb367fa1ee1f98d Mon Sep 17 00:00:00 2001 From: Marvis Date: Sun, 2 Aug 2026 15:01:07 +0200 Subject: [PATCH 7/8] Lift cmpeqb, cmprb, mfocrf, and mtocrf - cmpeqb: EQ of the target CR field set from an OR of the 8 byte compares; cmprb: EQ from the range check(s) of rA's low byte against the bounds in rB, honoring the L operand - mfocrf: builds the selected CR field's bits in their CR positions; mtocrf: shares the mtcrf lifting (same per-field flag writes) - mtocrf's decoded operand order was rS, FXM - reversed from mtcrf, the assembler's grammar (mtocrf NUM, GPR), and the ISA syntax; now decodes as FXM, rS --- arch/powerpc/decode/operands.c | 2 +- arch/powerpc/il.cpp | 85 ++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/decode/operands.c b/arch/powerpc/decode/operands.c index 8905ab5e59..331719f8b6 100644 --- a/arch/powerpc/decode/operands.c +++ b/arch/powerpc/decode/operands.c @@ -1540,8 +1540,8 @@ void FillOperands32(Instruction* instruction, uint32_t word32, uint64_t address) { uint32_t fxm = (word32 >> 12) & 0xff; - PushRS(instruction, word32); PushUIMMValue(instruction, fxm); + PushRS(instruction, word32); break; } diff --git a/arch/powerpc/il.cpp b/arch/powerpc/il.cpp index 18be586ac9..5e0bbe441c 100644 --- a/arch/powerpc/il.cpp +++ b/arch/powerpc/il.cpp @@ -871,6 +871,63 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, il.AddInstruction(ei2); break; + /* EQ of crfD = any byte of rB equals the low byte of rA; LT/GT/SO = 0 */ + case PPC_ID_CMPEQB: + { + REQUIRE3OPS + uint32_t crf = oper0->reg - PPC_REG_CRF0; + + ei0 = BN_INVALID_EXPR; + for (uint32_t byteIdx = 0; byteIdx < 8; byteIdx++) + { + ei1 = il.CompareEqual(1, + il.LowPart(1, il.LogicalShiftRight(8, il.Register(8, oper2->reg), il.Const(1, 8*byteIdx))), + il.LowPart(1, il.Register(8, oper1->reg))); + ei0 = (byteIdx == 0) ? ei1 : il.Or(0, ei0, ei1); + } + + il.AddInstruction(il.SetFlag(4*crf + IL_FLAG_LT, il.Const(0, 0))); + il.AddInstruction(il.SetFlag(4*crf + IL_FLAG_GT, il.Const(0, 0))); + il.AddInstruction(il.SetFlag(4*crf + IL_FLAG_EQ, ei0)); + il.AddInstruction(il.SetFlag(4*crf + IL_FLAG_SO, il.Const(0, 0))); + break; + } + + /* EQ of crfD = low byte of rA within the byte range(s) held in rB's low + word: bytes 1:0 are one range's hi:lo bound, and with L=0 bytes 3:2 + form a second accepted range; LT/GT/SO = 0 */ + case PPC_ID_CMPRB: + { + REQUIRE4OPS + uint32_t crf = oper0->reg - PPC_REG_CRF0; + + ei0 = il.And(0, + il.CompareUnsignedGreaterEqual(1, + il.LowPart(1, il.Register(4, oper2->reg)), + il.LowPart(1, il.Register(4, oper3->reg))), + il.CompareUnsignedLessEqual(1, + il.LowPart(1, il.Register(4, oper2->reg)), + il.LowPart(1, il.LogicalShiftRight(4, il.Register(4, oper3->reg), il.Const(1, 8))))); + + if (oper1->uimm == 0) + { + ei1 = il.And(0, + il.CompareUnsignedGreaterEqual(1, + il.LowPart(1, il.Register(4, oper2->reg)), + il.LowPart(1, il.LogicalShiftRight(4, il.Register(4, oper3->reg), il.Const(1, 16)))), + il.CompareUnsignedLessEqual(1, + il.LowPart(1, il.Register(4, oper2->reg)), + il.LowPart(1, il.LogicalShiftRight(4, il.Register(4, oper3->reg), il.Const(1, 24))))); + ei0 = il.Or(0, ei0, ei1); + } + + il.AddInstruction(il.SetFlag(4*crf + IL_FLAG_LT, il.Const(0, 0))); + il.AddInstruction(il.SetFlag(4*crf + IL_FLAG_GT, il.Const(0, 0))); + il.AddInstruction(il.SetFlag(4*crf + IL_FLAG_EQ, ei0)); + il.AddInstruction(il.SetFlag(4*crf + IL_FLAG_SO, il.Const(0, 0))); + break; + } + case PPC_ID_CMPWI: /* compare (signed) word(32-bit) immediate */ REQUIRE3OPS ei0 = operToIL(il, oper1); @@ -1043,6 +1100,7 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, } case PPC_ID_MTCRF: + case PPC_ID_MTOCRF: REQUIRE2OPS for (uint8_t test = 0x80, i = 0; test; test >>= 1, i++) { @@ -1054,6 +1112,33 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, } break; + /* rD = the selected CR field's bits, in their CR positions */ + case PPC_ID_MFOCRF: + { + REQUIRE2OPS + ei0 = BN_INVALID_EXPR; + for (uint32_t n = 0; n < 8; n++) + { + if ((oper1->uimm & (0x80u >> n)) == 0) + continue; + + ei1 = il.Or(4, + il.FlagBit(4, 4*n + IL_FLAG_LT, 31 - 4*n), + il.Or(4, + il.FlagBit(4, 4*n + IL_FLAG_GT, 30 - 4*n), + il.Or(4, + il.FlagBit(4, 4*n + IL_FLAG_EQ, 29 - 4*n), + il.FlagBit(4, 4*n + IL_FLAG_SO, 28 - 4*n)))); + ei0 = (ei0 == BN_INVALID_EXPR) ? ei1 : il.Or(4, ei0, ei1); + } + + if (ei0 == BN_INVALID_EXPR) + ei0 = il.Const(4, 0); + + il.AddInstruction(il.SetRegister(4, oper0->reg, ei0)); + break; + } + case PPC_ID_EXTSBx: case PPC_ID_EXTSHx: REQUIRE2OPS From 6704e4eb15c0ab6ada666532a5287ddcf2e102b2 Mon Sep 17 00:00:00 2001 From: Marvis Date: Sun, 2 Aug 2026 15:07:25 +0200 Subject: [PATCH 8/8] Model PowerPC system and barrier instructions with intrinsics Follows the __set_reservation/__check_reservation pattern: - memory barriers: sync/lwsync/ptesync/eieio/isync/mbar - cache maintenance with visible effects: dcbz/dcbzl (block zeroing), dcbf/dcbst/dcbi/icbi, all taking the effective address; pure hints (dcbt/dcbtt/dcbtst/dcbtstt/dcba) lift to nops - bit operations without LLIL equivalents: popcntb/popcntw/popcntd, cmpb, bpermd, darn - time base reads: mftb/mftbu - MSR/SPR access: mfmsr and mfspr upgraded from "rD = unimplemented" placeholders to intrinsics; mtmsr/mtmsrd and mtspr newly lifted - FPSCR access: mffs/mtfsf/mtfsb0/mtfsb1 mfxer/mtxer are lifted in pure IL instead, assembling and splitting the modeled SO/OV/CA flags at their XER bit positions. --- arch/powerpc/arch_ppc.cpp | 99 +++++++++++++++++++ arch/powerpc/il.cpp | 196 +++++++++++++++++++++++++++++++++++++- arch/powerpc/il.h | 27 ++++++ 3 files changed, 320 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/arch_ppc.cpp b/arch/powerpc/arch_ppc.cpp index bf2758d6a0..2698c7ed7b 100644 --- a/arch/powerpc/arch_ppc.cpp +++ b/arch/powerpc/arch_ppc.cpp @@ -671,6 +671,60 @@ class PowerpcArchitecture: public Architecture return "__set_reservation"; case PPC_INTRIN_CHECK_RESERVATION: return "__check_reservation"; + case PPC_INTRIN_SYNC: + return "__sync"; + case PPC_INTRIN_LWSYNC: + return "__lwsync"; + case PPC_INTRIN_PTESYNC: + return "__ptesync"; + case PPC_INTRIN_EIEIO: + return "__eieio"; + case PPC_INTRIN_ISYNC: + return "__isync"; + case PPC_INTRIN_MBAR: + return "__mbar"; + case PPC_INTRIN_DCBZ: + return "__dcbz"; + case PPC_INTRIN_DCBF: + return "__dcbf"; + case PPC_INTRIN_DCBST: + return "__dcbst"; + case PPC_INTRIN_DCBI: + return "__dcbi"; + case PPC_INTRIN_ICBI: + return "__icbi"; + case PPC_INTRIN_POPCNTB: + return "__popcntb"; + case PPC_INTRIN_POPCNTW: + return "__popcntw"; + case PPC_INTRIN_POPCNTD: + return "__popcntd"; + case PPC_INTRIN_CMPB: + return "__cmpb"; + case PPC_INTRIN_BPERMD: + return "__bpermd"; + case PPC_INTRIN_DARN: + return "__darn"; + case PPC_INTRIN_MFTB: + return "__mftb"; + case PPC_INTRIN_MFTBU: + return "__mftbu"; + case PPC_INTRIN_MFMSR: + return "__mfmsr"; + case PPC_INTRIN_MTMSR: + return "__mtmsr"; + case PPC_INTRIN_MFSPR: + return "__mfspr"; + case PPC_INTRIN_MTSPR: + return "__mtspr"; + case PPC_INTRIN_MFFS: + return "__mffs"; + case PPC_INTRIN_MTFSF: + return "__mtfsf"; + case PPC_INTRIN_MTFSB0: + return "__mtfsb0"; + case PPC_INTRIN_MTFSB1: + return "__mtfsb1"; default: if ((decodeFlags & DECODE_FLAGS_PS)) { @@ -730,7 +784,37 @@ class PowerpcArchitecture: public Architecture return {NameAndType(Type::FloatType(4))}; case PPC_INTRIN_SET_RESERVATION: case PPC_INTRIN_CHECK_RESERVATION: + case PPC_INTRIN_DCBZ: + case PPC_INTRIN_DCBF: + case PPC_INTRIN_DCBST: + case PPC_INTRIN_DCBI: + case PPC_INTRIN_ICBI: return {NameAndType("address", Type::IntegerType(addressSize, false))}; + case PPC_INTRIN_POPCNTB: + case PPC_INTRIN_POPCNTW: + case PPC_INTRIN_POPCNTD: + return {NameAndType(Type::IntegerType(addressSize, false))}; + case PPC_INTRIN_CMPB: + return {NameAndType(Type::IntegerType(addressSize, false)), + NameAndType(Type::IntegerType(addressSize, false))}; + case PPC_INTRIN_BPERMD: + return {NameAndType(Type::IntegerType(8, false)), + NameAndType(Type::IntegerType(8, false))}; + case PPC_INTRIN_DARN: + return {NameAndType("l", Type::IntegerType(4, false))}; + case PPC_INTRIN_MTMSR: + return {NameAndType("msr", Type::IntegerType(addressSize, false))}; + case PPC_INTRIN_MFSPR: + return {NameAndType("spr", Type::IntegerType(4, false))}; + case PPC_INTRIN_MTSPR: + return {NameAndType("spr", Type::IntegerType(4, false)), + NameAndType("value", Type::IntegerType(addressSize, false))}; + case PPC_INTRIN_MTFSF: + return {NameAndType("mask", Type::IntegerType(4, false)), + NameAndType("value", Type::FloatType(8))}; + case PPC_INTRIN_MTFSB0: + case PPC_INTRIN_MTFSB1: + return {NameAndType("bit", Type::IntegerType(4, false))}; // for now, quantize is operating on the float in, and the gqr that holds the scale default: if ((decodeFlags & DECODE_FLAGS_PS)) @@ -765,6 +849,21 @@ class PowerpcArchitecture: public Architecture return {Type::FloatType(4)}; case PPC_INTRIN_CHECK_RESERVATION: return {Type::IntegerType(0, false)}; + case PPC_INTRIN_POPCNTB: + case PPC_INTRIN_POPCNTW: + case PPC_INTRIN_POPCNTD: + case PPC_INTRIN_CMPB: + case PPC_INTRIN_MFTB: + case PPC_INTRIN_MFMSR: + case PPC_INTRIN_MFSPR: + return {Type::IntegerType(addressSize, false)}; + case PPC_INTRIN_BPERMD: + case PPC_INTRIN_DARN: + return {Type::IntegerType(8, false)}; + case PPC_INTRIN_MFTBU: + return {Type::IntegerType(4, false)}; + case PPC_INTRIN_MFFS: + return {Type::FloatType(8)}; default: if ((decodeFlags & DECODE_FLAGS_PS)) { diff --git a/arch/powerpc/il.cpp b/arch/powerpc/il.cpp index 5e0bbe441c..2932d8ed01 100644 --- a/arch/powerpc/il.cpp +++ b/arch/powerpc/il.cpp @@ -1076,12 +1076,27 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, case PPC_ID_MFSPR: REQUIRE2OPS - il.AddInstruction(il.SetRegister(4, oper0->reg, il.Unimplemented())); + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(oper0->reg)}, + PPC_INTRIN_MFSPR, {il.Const(4, oper1->uimm)})); + break; + + case PPC_ID_MTSPR: + REQUIRE2OPS + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_MTSPR, + {il.Const(4, oper0->uimm), operToIL_a(il, oper1, addressSize_l)})); break; case PPC_ID_MFMSR: REQUIRE1OP - il.AddInstruction(il.SetRegister(4, oper0->reg, il.Unimplemented())); + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(oper0->reg)}, + PPC_INTRIN_MFMSR, {})); + break; + + case PPC_ID_MTMSR: + case PPC_ID_MTMSRD: + REQUIRE1OP + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_MTMSR, + {operToIL_a(il, oper0, addressSize_l)})); break; case PPC_ID_MCRF: @@ -2530,6 +2545,183 @@ bool GetLowLevelILForPPCInstruction(Architecture *arch, LowLevelILFunction &il, break; } + // ===================================== + // memory barriers + // ===================================== + + case PPC_ID_SYNC: + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_SYNC, {})); + break; + + case PPC_ID_LWSYNC: + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_LWSYNC, {})); + break; + + case PPC_ID_PTESYNC: + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_PTESYNC, {})); + break; + + case PPC_ID_EIEIO: + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_EIEIO, {})); + break; + + case PPC_ID_ISYNC: + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_ISYNC, {})); + break; + + case PPC_ID_MBAR: + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_MBAR, {})); + break; + + // ===================================== + // cache management + // ===================================== + + /* maintenance ops with visible memory effects (dcbz zeroes a block) */ + case PPC_ID_DCBZ: + case PPC_ID_DCBZL: + case PPC_ID_DCBF: + case PPC_ID_DCBST: + case PPC_ID_DCBI: + case PPC_ID_ICBI: + { + REQUIRE2OPS + uint32_t intrin; + switch (instruction->id) + { + case PPC_ID_DCBZ: + case PPC_ID_DCBZL: intrin = PPC_INTRIN_DCBZ; break; + case PPC_ID_DCBF: intrin = PPC_INTRIN_DCBF; break; + case PPC_ID_DCBST: intrin = PPC_INTRIN_DCBST; break; + case PPC_ID_DCBI: intrin = PPC_INTRIN_DCBI; break; + default: intrin = PPC_INTRIN_ICBI; break; + } + + il.AddInstruction(il.Intrinsic({}, intrin, + {il.Add(addressSize_l, + operToIL(il, oper0, OTI_GPR0_ZERO, PPC_IL_EXTRA_DEFAULT, addressSize_l), + operToIL_a(il, oper1, addressSize_l))})); + break; + } + + /* pure prefetch/placement hints */ + case PPC_ID_DCBT: + case PPC_ID_DCBTT: + case PPC_ID_DCBTST: + case PPC_ID_DCBTSTT: + case PPC_ID_DCBA: + il.AddInstruction(il.Nop()); + break; + + // ===================================== + // bit operations without LLIL equivalents + // ===================================== + + case PPC_ID_POPCNTB: + case PPC_ID_POPCNTW: + case PPC_ID_POPCNTD: + { + REQUIRE2OPS + uint32_t intrin; + switch (instruction->id) + { + case PPC_ID_POPCNTB: intrin = PPC_INTRIN_POPCNTB; break; + case PPC_ID_POPCNTW: intrin = PPC_INTRIN_POPCNTW; break; + default: intrin = PPC_INTRIN_POPCNTD; break; + } + + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(oper0->reg)}, + intrin, {operToIL_a(il, oper1, addressSize_l)})); + break; + } + + case PPC_ID_CMPB: + REQUIRE3OPS + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(oper0->reg)}, + PPC_INTRIN_CMPB, + {operToIL_a(il, oper1, addressSize_l), operToIL_a(il, oper2, addressSize_l)})); + break; + + case PPC_ID_BPERMD: + REQUIRE3OPS + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(oper0->reg)}, + PPC_INTRIN_BPERMD, + {operToIL_a(il, oper1, 8), operToIL_a(il, oper2, 8)})); + break; + + case PPC_ID_DARN: + REQUIRE2OPS + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(oper0->reg)}, + PPC_INTRIN_DARN, {il.Const(4, oper1->uimm)})); + break; + + // ===================================== + // time base + // ===================================== + + case PPC_ID_MFTB: + REQUIRE1OP + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(oper0->reg)}, + PPC_INTRIN_MFTB, {})); + break; + + case PPC_ID_MFTBU: + REQUIRE1OP + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(oper0->reg)}, + PPC_INTRIN_MFTBU, {})); + break; + + // ===================================== + // FPSCR access + // ===================================== + + case PPC_ID_MFFSx: + REQUIRE1OP + il.AddInstruction(il.Intrinsic({RegisterOrFlag::Register(oper0->reg)}, + PPC_INTRIN_MFFS, {})); + break; + + case PPC_ID_MTFSFx: + REQUIRE2OPS + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_MTFSF, + {il.Const(4, oper0->uimm), operToIL_a(il, oper1, 8)})); + break; + + case PPC_ID_MTFSB0x: + REQUIRE1OP + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_MTFSB0, {il.Const(4, oper0->uimm)})); + break; + + case PPC_ID_MTFSB1x: + REQUIRE1OP + il.AddInstruction(il.Intrinsic({}, PPC_INTRIN_MTFSB1, {il.Const(4, oper0->uimm)})); + break; + + // ===================================== + // XER access + // ===================================== + + /* rD = SO | OV | CA assembled into their XER bit positions */ + case PPC_ID_MFXER: + REQUIRE1OP + ei0 = il.Or(4, + il.FlagBit(4, IL_FLAG_XER_SO, 31), + il.Or(4, + il.FlagBit(4, IL_FLAG_XER_OV, 30), + il.FlagBit(4, IL_FLAG_XER_CA, 29))); + il.AddInstruction(il.SetRegister(4, oper0->reg, ei0)); + break; + + case PPC_ID_MTXER: + REQUIRE1OP + il.AddInstruction(il.SetFlag(IL_FLAG_XER_SO, + il.TestBit(4, operToIL(il, oper0), il.Const(1, 31)))); + il.AddInstruction(il.SetFlag(IL_FLAG_XER_OV, + il.TestBit(4, operToIL(il, oper0), il.Const(1, 30)))); + il.AddInstruction(il.SetFlag(IL_FLAG_XER_CA, + il.TestBit(4, operToIL(il, oper0), il.Const(1, 29)))); + break; + case PPC_ID_MODSW: REQUIRE3OPS ei0 = il.ModSigned(4, il.Register(4, oper1->reg), il.Register(4, oper2->reg)); diff --git a/arch/powerpc/il.h b/arch/powerpc/il.h index d747514427..dda25efe5b 100644 --- a/arch/powerpc/il.h +++ b/arch/powerpc/il.h @@ -214,6 +214,33 @@ enum PPCIntrinsic : uint32_t PPC_INTRIN_FRSP, PPC_INTRIN_SET_RESERVATION, // lwarx/ldarx: establish a reservation PPC_INTRIN_CHECK_RESERVATION, // stwcx./stdcx.: is the reservation still held? + PPC_INTRIN_SYNC, + PPC_INTRIN_LWSYNC, + PPC_INTRIN_PTESYNC, + PPC_INTRIN_EIEIO, + PPC_INTRIN_ISYNC, + PPC_INTRIN_MBAR, + PPC_INTRIN_DCBZ, // zero a cache block + PPC_INTRIN_DCBF, + PPC_INTRIN_DCBST, + PPC_INTRIN_DCBI, + PPC_INTRIN_ICBI, + PPC_INTRIN_POPCNTB, + PPC_INTRIN_POPCNTW, + PPC_INTRIN_POPCNTD, + PPC_INTRIN_CMPB, + PPC_INTRIN_BPERMD, + PPC_INTRIN_DARN, + PPC_INTRIN_MFTB, + PPC_INTRIN_MFTBU, + PPC_INTRIN_MFMSR, + PPC_INTRIN_MTMSR, + PPC_INTRIN_MFSPR, + PPC_INTRIN_MTSPR, + PPC_INTRIN_MFFS, + PPC_INTRIN_MTFSF, + PPC_INTRIN_MTFSB0, + PPC_INTRIN_MTFSB1, PPC_INTRIN_END, PPC_PS_INTRIN_QUANTIZE, PPC_PS_INTRIN_DEQUANTIZE,