Skip to content

Commit 2ab6dbb

Browse files
committed
chore: refactor ValueAccumukator logics
1 parent 554249c commit 2ab6dbb

3 files changed

Lines changed: 570 additions & 84 deletions

File tree

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
using AsmArm64;
2+
using Gee.External.Capstone.Arm64;
3+
using System.Buffers.Binary;
4+
using System.Diagnostics;
5+
using System.Diagnostics.CodeAnalysis;
6+
using AsmArm64Instruction = AsmArm64.Arm64Instruction;
7+
using AsmArm64Operand = AsmArm64.Arm64Operand;
8+
using Arm64InstructionId = AsmArm64.Arm64InstructionId;
9+
using CapstoneArm64Instruction = Gee.External.Capstone.Arm64.Arm64Instruction;
10+
using CapstoneArm64InstructionId = Gee.External.Capstone.Arm64.Arm64InstructionId;
11+
using CapstoneArm64Operand = Gee.External.Capstone.Arm64.Arm64Operand;
12+
using CapstoneArm64OperandType = Gee.External.Capstone.Arm64.Arm64OperandType;
13+
using CapstoneArm64RegisterId = Gee.External.Capstone.Arm64.Arm64RegisterId;
14+
15+
namespace BenchmarkDotNet.Disassemblers;
16+
17+
internal static class Arm64InstructionExtensions
18+
{
19+
#region Extension methods for Capstone
20+
extension(CapstoneArm64Instruction instruction)
21+
{
22+
public CapstoneArm64RegisterId GetRegisterId(int index)
23+
{
24+
var operands = instruction.Details.Operands;
25+
if ((uint)index >= (uint)operands.Length)
26+
throw new ArgumentOutOfRangeException(nameof(index));
27+
28+
return operands[index].Register.Id;
29+
}
30+
31+
public Arm64ShiftOperation GetShiftOperation(int index)
32+
{
33+
var operands = instruction.Details.Operands;
34+
if ((uint)index >= (uint)operands.Length)
35+
throw new ArgumentOutOfRangeException(nameof(index));
36+
37+
var operand = operands[index];
38+
var shiftOperation = operand.ShiftOperation;
39+
40+
switch (instruction.Id)
41+
{
42+
case CapstoneArm64InstructionId.ARM64_INS_ADD:
43+
case CapstoneArm64InstructionId.ARM64_INS_ADRP:
44+
case CapstoneArm64InstructionId.ARM64_INS_MOVK:
45+
case CapstoneArm64InstructionId.ARM64_INS_MOVZ:
46+
switch (shiftOperation)
47+
{
48+
case Arm64ShiftOperation.ARM64_SFT_LSL:
49+
return shiftOperation;
50+
51+
case Arm64ShiftOperation.Invalid:
52+
if (operand.ShiftValue == 0)
53+
return Arm64ShiftOperation.ARM64_SFT_LSL; // Capstone represents LSL #0 as ARM64_SFT_INVALID.
54+
goto default;
55+
56+
// Currently used instructions don't accept other shift operations.
57+
default:
58+
throw new NotSupportedException($"Unexpected shift operation({shiftOperation}) is specified on instruction({instruction.Id})");
59+
}
60+
61+
// Currently other instructions are not supported.
62+
default:
63+
throw new UnreachableException($"Unexpected instruction({instruction.Id}) is specified");
64+
}
65+
}
66+
67+
public int GetShiftAmount(int index)
68+
{
69+
var operands = instruction.Details.Operands;
70+
if ((uint)index >= (uint)operands.Length)
71+
throw new ArgumentOutOfRangeException(nameof(index));
72+
73+
var operand = operands[index];
74+
75+
// Capstone represents LSL #0 as ARM64_SFT_INVALID.
76+
if (operand.ShiftOperation == Arm64ShiftOperation.Invalid)
77+
return 0;
78+
79+
return operand.ShiftValue;
80+
}
81+
82+
public long GetRawImmediateValue(int index)
83+
{
84+
var operands = instruction.Details.Operands;
85+
if ((uint)index >= (uint)operands.Length)
86+
throw new ArgumentOutOfRangeException(nameof(index));
87+
88+
return operands[index].Immediate;
89+
}
90+
91+
public long GetShiftedImmediateValue(int index)
92+
{
93+
var operands = instruction.Details.Operands;
94+
if ((uint)index >= (uint)operands.Length)
95+
throw new ArgumentOutOfRangeException(nameof(index));
96+
97+
var operand = operands[index];
98+
99+
var shiftOperation = instruction.GetShiftOperation(index);
100+
switch (shiftOperation)
101+
{
102+
case Arm64ShiftOperation.ARM64_SFT_LSL:
103+
var shiftAmount = instruction.GetShiftAmount(index);
104+
return operand.Immediate << shiftAmount;
105+
default:
106+
throw new NotSupportedException($"Unexpected shift operation({shiftOperation}) is specified on instruction({instruction.Id})");
107+
}
108+
}
109+
110+
public bool TryGetOperand(
111+
int index,
112+
CapstoneArm64OperandType type,
113+
[NotNullWhen(true)] out CapstoneArm64Operand? result)
114+
{
115+
var operands = instruction.Details.Operands;
116+
if ((uint)index >= (uint)operands.Length)
117+
{
118+
result = null;
119+
return false;
120+
}
121+
122+
result = operands[index];
123+
if (result.Type == type)
124+
return true;
125+
126+
result = null;
127+
return false;
128+
}
129+
130+
public bool BelongsToGroup(Arm64InstructionGroupId expectedGroup)
131+
{
132+
if (instruction.Bytes.Length < 4)
133+
return false;
134+
var rawInstruction = BinaryPrimitives.ReadUInt32LittleEndian(instruction.Bytes);
135+
return AsmArm64Instruction.Decode(rawInstruction).BelongsToGroup(expectedGroup);
136+
}
137+
138+
public bool IsLdrFromTrackedRegister(CapstoneArm64RegisterId trackedRegister)
139+
{
140+
if (instruction.Id != CapstoneArm64InstructionId.ARM64_INS_LDR)
141+
return false;
142+
143+
if (!instruction.TryGetOperand(1, CapstoneArm64OperandType.Memory, out var memoryOperand))
144+
return false;
145+
146+
// Check base register is the register that is currently tracked.
147+
var memory = memoryOperand.Memory;
148+
if (memory.Base.Id != trackedRegister)
149+
return false;
150+
151+
// Check displacement.
152+
if (memory.Displacement != 0)
153+
return false;
154+
155+
// Check index register is specified.
156+
if (memory.Index != null)
157+
return false;
158+
159+
// Ldr from tracked register (with no displacement/index register).
160+
return true;
161+
}
162+
163+
public bool IsConditionalBranch()
164+
{
165+
switch (instruction.Id)
166+
{
167+
case Gee.External.Capstone.Arm64.Arm64InstructionId.ARM64_INS_CBZ:
168+
case Gee.External.Capstone.Arm64.Arm64InstructionId.ARM64_INS_CBNZ:
169+
case Gee.External.Capstone.Arm64.Arm64InstructionId.ARM64_INS_TBZ:
170+
case Gee.External.Capstone.Arm64.Arm64InstructionId.ARM64_INS_TBNZ:
171+
return true;
172+
173+
case Gee.External.Capstone.Arm64.Arm64InstructionId.ARM64_INS_B:
174+
switch (instruction.Details.ConditionCode)
175+
{
176+
// `B` and `B.AL` instructions is unconditional branch.
177+
case Gee.External.Capstone.Arm64.Arm64ConditionCode.Invalid:
178+
case Gee.External.Capstone.Arm64.Arm64ConditionCode.ARM64_CC_AL:
179+
return false;
180+
default:
181+
return true;
182+
}
183+
184+
default:
185+
return false;
186+
}
187+
}
188+
189+
public bool IsUnconditionalControlFlow()
190+
{
191+
return instruction.BelongsToGroup(Arm64InstructionGroupId.ARM64_GRP_BRANCH_RELATIVE)
192+
|| instruction.BelongsToGroup(Arm64InstructionGroupId.ARM64_GRP_CALL)
193+
|| instruction.BelongsToGroup(Arm64InstructionGroupId.ARM64_GRP_JUMP);
194+
}
195+
196+
}
197+
#endregion
198+
199+
#region Extension methods for AsmArm64
200+
extension(AsmArm64Instruction instruction)
201+
{
202+
public Arm64RegisterOperand GetRegisterOperand(int index)
203+
=> (Arm64RegisterOperand)instruction.GetOperand(index, Arm64OperandKind.Register);
204+
205+
public Arm64ImmediateOperand GetImmediateOperand(int index)
206+
=> (Arm64ImmediateOperand)instruction.GetOperand(index, Arm64OperandKind.Immediate);
207+
208+
private AsmArm64Operand GetOperand(int index, Arm64OperandKind expectedKind)
209+
{
210+
var operand = instruction.GetOperand(index);
211+
if (operand.Kind != expectedKind)
212+
throw new ArgumentException($"The operand at index {index} is not of the expected kind {expectedKind}. Actual kind: {operand.Kind}");
213+
214+
return operand;
215+
}
216+
217+
public bool BelongsToGroup(Arm64InstructionGroupId expectedGroup)
218+
{
219+
// AsmArm64 don't provide mapping for instruction groups.
220+
// So it need to simulate Capstone's BelongsToGroup behavior.
221+
// Capstone's group mappings are defined at https://github.com/capstone-engine/capstone/blob/6.0.0-Alpha10/arch/AArch64/AArch64GenCSMappingInsn.inc
222+
switch (expectedGroup)
223+
{
224+
case Arm64InstructionGroupId.ARM64_GRP_JUMP:
225+
switch (instruction.Id)
226+
{
227+
case Arm64InstructionId.B_only_branch_imm:
228+
case Arm64InstructionId.B_only_condbranch:
229+
case Arm64InstructionId.BC_only_condbranch:
230+
case Arm64InstructionId.BR_64_branch_reg:
231+
case Arm64InstructionId.BRAA_64p_branch_reg:
232+
case Arm64InstructionId.BRAAZ_64_branch_reg:
233+
case Arm64InstructionId.BRAB_64p_branch_reg:
234+
case Arm64InstructionId.BRABZ_64_branch_reg:
235+
case Arm64InstructionId.CBNZ_32_compbranch:
236+
case Arm64InstructionId.CBNZ_64_compbranch:
237+
case Arm64InstructionId.CBZ_32_compbranch:
238+
case Arm64InstructionId.CBZ_64_compbranch:
239+
case Arm64InstructionId.DRPS_64e_branch_reg:
240+
case Arm64InstructionId.ERET_64e_branch_reg:
241+
case Arm64InstructionId.ERETAA_64e_branch_reg:
242+
case Arm64InstructionId.ERETAB_64e_branch_reg:
243+
case Arm64InstructionId.RET_64r_branch_reg:
244+
case Arm64InstructionId.RETAA_64e_branch_reg:
245+
case Arm64InstructionId.RETAASPPCR_64m_branch_reg:
246+
case Arm64InstructionId.RETAASPPC_only_miscbranch:
247+
case Arm64InstructionId.RETAB_64e_branch_reg:
248+
case Arm64InstructionId.RETABSPPCR_64m_branch_reg:
249+
case Arm64InstructionId.RETABSPPC_only_miscbranch:
250+
case Arm64InstructionId.TBNZ_only_testbranch:
251+
case Arm64InstructionId.TBZ_only_testbranch:
252+
return true;
253+
254+
default:
255+
return false;
256+
}
257+
258+
case Arm64InstructionGroupId.ARM64_GRP_CALL:
259+
switch (instruction.Id)
260+
{
261+
case Arm64InstructionId.BL_only_branch_imm:
262+
case Arm64InstructionId.BLR_64_branch_reg: // BLR: Branch with link
263+
case Arm64InstructionId.BLRAA_64p_branch_reg: // BLRAA: Branch with link to register with PAC
264+
case Arm64InstructionId.BLRAB_64p_branch_reg: // BLRAB: Branch with link to register with PAC
265+
case Arm64InstructionId.BLRAAZ_64_branch_reg: // Z: Zero modifier
266+
case Arm64InstructionId.BLRABZ_64_branch_reg: // Z: Zero modifier
267+
case Arm64InstructionId.HVC_ex_exception:
268+
case Arm64InstructionId.SMC_ex_exception:
269+
case Arm64InstructionId.SVC_ex_exception:
270+
return true;
271+
default:
272+
return false;
273+
}
274+
case Arm64InstructionGroupId.ARM64_GRP_RET:
275+
switch (instruction.Id)
276+
{
277+
case Arm64InstructionId.DRPS_64e_branch_reg:
278+
case Arm64InstructionId.ERET_64e_branch_reg:
279+
case Arm64InstructionId.ERETAA_64e_branch_reg:
280+
case Arm64InstructionId.ERETAB_64e_branch_reg:
281+
case Arm64InstructionId.RET_64r_branch_reg:
282+
case Arm64InstructionId.RETAA_64e_branch_reg:
283+
case Arm64InstructionId.RETAASPPC_only_miscbranch:
284+
case Arm64InstructionId.RETAASPPCR_64m_branch_reg:
285+
case Arm64InstructionId.RETAB_64e_branch_reg:
286+
case Arm64InstructionId.RETABSPPC_only_miscbranch:
287+
case Arm64InstructionId.RETABSPPCR_64m_branch_reg:
288+
return true;
289+
default:
290+
return false;
291+
}
292+
case Arm64InstructionGroupId.ARM64_GRP_BRANCH_RELATIVE:
293+
switch (instruction.Id)
294+
{
295+
case Arm64InstructionId.B_only_branch_imm:
296+
case Arm64InstructionId.B_only_condbranch:
297+
case Arm64InstructionId.BC_only_condbranch:
298+
case Arm64InstructionId.BL_only_branch_imm:
299+
case Arm64InstructionId.CBNZ_32_compbranch:
300+
case Arm64InstructionId.CBNZ_64_compbranch:
301+
case Arm64InstructionId.CBZ_32_compbranch:
302+
case Arm64InstructionId.CBZ_64_compbranch:
303+
case Arm64InstructionId.RETAASPPC_only_miscbranch:
304+
case Arm64InstructionId.RETABSPPC_only_miscbranch:
305+
case Arm64InstructionId.TBNZ_only_testbranch:
306+
case Arm64InstructionId.TBZ_only_testbranch:
307+
return true;
308+
default:
309+
return false;
310+
}
311+
312+
// Currently, other group ids are not supported.
313+
case Arm64InstructionGroupId.Invalid:
314+
case Arm64InstructionGroupId.ARM64_GRP_INT:
315+
case Arm64InstructionGroupId.ARM64_GRP_PRIVILEGE:
316+
case Arm64InstructionGroupId.ARM64_GRP_CRYPTO:
317+
case Arm64InstructionGroupId.ARM64_GRP_FPARMV8:
318+
case Arm64InstructionGroupId.ARM64_GRP_NEON:
319+
case Arm64InstructionGroupId.ARM64_GRP_CRC:
320+
default:
321+
throw new NotSupportedException($"Group {expectedGroup} is not supported.");
322+
}
323+
}
324+
325+
public IEnumerable<Arm64RegisterAny> GetWrittenRegisters()
326+
{
327+
foreach (var operand in instruction.Operands)
328+
{
329+
switch (operand.Kind)
330+
{
331+
case Arm64OperandKind.Register:
332+
var registerOperand = (Arm64RegisterOperand)operand;
333+
334+
if ((registerOperand.Flags & Arm64OperandFlags.Write) != 0)
335+
yield return registerOperand.Value;
336+
337+
continue;
338+
339+
case Arm64OperandKind.RegisterGroup:
340+
var registerGroupOperand = (Arm64RegisterGroupOperand)operand;
341+
342+
if ((registerGroupOperand.Flags & Arm64OperandFlags.Write) != 0)
343+
{
344+
var registerGroupAny = registerGroupOperand.Value;
345+
var baseRegister = registerGroupAny.BaseRegister;
346+
for (int i = 0; i < registerGroupAny.Count; ++i)
347+
{
348+
yield return Arm64RegisterAny.Create(baseRegister.Kind, baseRegister.Index + i, baseRegister.VKind, baseRegister.ElementCount, baseRegister.ElementIndex);
349+
}
350+
}
351+
continue;
352+
353+
default:
354+
continue;
355+
}
356+
}
357+
}
358+
}
359+
#endregion
360+
}

0 commit comments

Comments
 (0)