• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SamboyCoding / Cpp2IL / 15173156024

21 May 2025 09:41PM UTC coverage: 34.291% (+0.2%) from 34.062%
15173156024

Pull #462

github

web-flow
Merge 01e84d727 into 5807d2b6c
Pull Request #462: Support overriding anything

1801 of 6646 branches covered (27.1%)

Branch coverage included in aggregate %.

133 of 243 new or added lines in 35 files covered. (54.73%)

5 existing lines in 5 files now uncovered.

4201 of 10857 relevant lines covered (38.69%)

186186.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Cpp2IL.Core/InstructionSets/NewArmV8InstructionSet.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Diagnostics;
4
using System.Linq;
5
using Disarm;
6
using Cpp2IL.Core.Api;
7
using Cpp2IL.Core.Il2CppApiFunctions;
8
using Cpp2IL.Core.ISIL;
9
using Cpp2IL.Core.Model.Contexts;
10
using Cpp2IL.Core.Utils;
11
using Disarm.InternalDisassembly;
12
using LibCpp2IL;
13

14
namespace Cpp2IL.Core.InstructionSets;
15

16
public class NewArmV8InstructionSet : Cpp2IlInstructionSet
17
{
18
    [ThreadStatic]
19
    private static Dictionary<Arm64Register, ulong> adrpOffsets = new();
×
20
    
21
    public override Memory<byte> GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator)
22
    {
23
        if (context is not ConcreteGenericMethodAnalysisContext)
×
24
        {
25
            //Managed method or attr gen => grab raw byte range between a and b
26
            var startOfNextFunction = (int)MiscUtils.GetAddressOfNextFunctionStart(context.UnderlyingPointer);
×
27
            var ptrAsInt = (int)context.UnderlyingPointer;
×
28
            var count = startOfNextFunction - ptrAsInt;
×
29

30
            if (startOfNextFunction > 0)
×
31
                return LibCpp2IlMain.Binary!.GetRawBinaryContent().AsMemory(ptrAsInt, count);
×
32
        }
33

34
        var result = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer);
×
35
        var endVa = result.LastValid().Address + 4;
×
36

37
        var start = (int)context.AppContext.Binary.MapVirtualAddressToRaw(context.UnderlyingPointer);
×
38
        var end = (int)context.AppContext.Binary.MapVirtualAddressToRaw(endVa);
×
39

40
        //Sanity check
41
        if (start < 0 || end < 0 || start >= context.AppContext.Binary.RawLength || end >= context.AppContext.Binary.RawLength)
×
42
            throw new Exception($"Failed to map virtual address 0x{context.UnderlyingPointer:X} to raw address for method {context!.DeclaringType?.FullName}/{context.Name} - start: 0x{start:X}, end: 0x{end:X} are out of bounds for length {context.AppContext.Binary.RawLength}.");
×
43

44
        return context.AppContext.Binary.GetRawBinaryContent().AsMemory(start, end - start);
×
45
    }
46

47
    public override List<InstructionSetIndependentInstruction> GetIsilFromMethod(MethodAnalysisContext context)
48
    {
49
        var insns = NewArm64Utils.GetArm64MethodBodyAtVirtualAddress(context.UnderlyingPointer);
×
50

51
        var builder = new IsilBuilder();
×
52

53
        if (adrpOffsets == null!) //Null suppress because thread static weirdness
×
54
            adrpOffsets = new();
×
55
        
56
        adrpOffsets.Clear();
×
57

58
        foreach (var instruction in insns)
×
59
        {
60
            ConvertInstructionStatement(instruction, builder, context);
×
61
        }
62
        
63
        adrpOffsets.Clear();
×
64

65
        builder.FixJumps();
×
66

67
        return builder.BackingStatementList;
×
68
    }
69

70
    private void ConvertInstructionStatement(Arm64Instruction instruction, IsilBuilder builder, MethodAnalysisContext context)
71
    {
72
        switch (instruction.Mnemonic)
×
73
        {
74
            case Arm64Mnemonic.MOV:
75
            case Arm64Mnemonic.MOVZ:
76
            case Arm64Mnemonic.FMOV:
77
            case Arm64Mnemonic.SXTW: // move and sign extend Wn to Xd
78
            case Arm64Mnemonic.LDR:
79
            case Arm64Mnemonic.LDRB:
80
                //Load and move are (dest, src)
81
                
82
                if (instruction.MemIsPreIndexed) //  such as  X8, [X19,#0x30]! 
×
83
                {
84
                    //Regardless of anything else, we're trashing any possible ADRP offsets in the dest here, so let's clear that
85
                    if(instruction.Op0Kind == Arm64OperandKind.Register)
×
86
                        adrpOffsets.Remove(instruction.Op0Reg);
×
87
                    
88
                    var operate = ConvertOperand(instruction, 1);
×
89
                    if (operate.Data is IsilMemoryOperand operand)
×
90
                    {
91
                        var register = operand.Base!.Value;
×
92
                        // X19= X19, #0x30
93
                        builder.Add(instruction.Address, register, register, InstructionSetIndependentOperand.MakeImmediate(operand.Addend));
×
94
                        //X8 = [X19]
95
                        builder.Move(instruction.Address, ConvertOperand(instruction, 0), InstructionSetIndependentOperand.MakeMemory(new IsilMemoryOperand(
×
96
                            InstructionSetIndependentOperand.MakeRegister(register.ToString()!.ToUpperInvariant()),
×
97
                            0)));
×
98
                        break;
×
99
                    }
100
                }
101

102
                if (instruction.Op1Kind == Arm64OperandKind.Memory && adrpOffsets.TryGetValue(instruction.MemBase, out var page) && instruction.MemOffset != 0 && instruction.MemAddendReg == Arm64Register.INVALID)
×
103
                {
104
                    //Maybe this is a bit hacky? But I really don't want to write paged load handling into ISIL itself, it's an Arm64 quirk
105
                    //LDR X0, [X1, #0x1000], where X1 was previously loaded with a page address via an ADRP instruction
106
                    //We just return the final address, it makes ISIL happier.
107
                    //TODO check if this is correct
108
                    var offset = instruction.MemOffset + (long) page;
×
109
                    
110
                    //We're also trashing any possible ADRP offsets in the dest here, so let's clear that now we've possibly grabbed the value if we need it (it's common to store the page and final address in the same register)
111
                    if(instruction.Op0Kind == Arm64OperandKind.Register)
×
112
                        adrpOffsets.Remove(instruction.Op0Reg);
×
113
                    
114
                    builder.Move(instruction.Address, ConvertOperand(instruction, 0), InstructionSetIndependentOperand.MakeMemory(new(offset)));
×
115
                    break;
×
116
                }
117

118
                //And again here we're trashing any possible ADRP offsets in the dest here, so let's clear that
119
                if(instruction.Op0Kind == Arm64OperandKind.Register)
×
120
                    adrpOffsets.Remove(instruction.Op0Reg);
×
121
                
122
                builder.Move(instruction.Address, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
×
123
                break;
×
124
            case Arm64Mnemonic.MOVN:
125
            {
126
                // dest = ~src
127
                
128
                //See above re: ADRP offsets
129
                if(instruction.Op0Kind == Arm64OperandKind.Register)
×
130
                    adrpOffsets.Remove(instruction.Op0Reg);
×
131
                
132
                var temp = InstructionSetIndependentOperand.MakeRegister("TEMP");
×
133
                builder.Move(instruction.Address, temp, ConvertOperand(instruction, 1));
×
134
                builder.Not(instruction.Address, temp);
×
135
                builder.Move(instruction.Address, ConvertOperand(instruction, 0), temp);
×
136
                break;
×
137
            }
138
            case Arm64Mnemonic.STR:
139
            case Arm64Mnemonic.STUR: // unscaled
140
            case Arm64Mnemonic.STRB:
141
                //Store is (src, dest)
142
                builder.Move(instruction.Address, ConvertOperand(instruction, 1), ConvertOperand(instruction, 0));
×
143
                break;
×
144
            case Arm64Mnemonic.STP:
145
                // store pair of registers (reg1, reg2, dest)
146
            {
147
                var dest = ConvertOperand(instruction, 2);
×
148
                if (dest.Data is IsilRegisterOperand { RegisterName: "X31" }) // if stack
×
149
                {
150
                    builder.Move(instruction.Address, dest, ConvertOperand(instruction, 0));
×
151
                    builder.Move(instruction.Address, dest, ConvertOperand(instruction, 1));
×
152
                }
153
                else if (dest.Data is IsilMemoryOperand memory)
×
154
                {
155
                    var firstRegister = ConvertOperand(instruction, 0);
×
156
                    long size = ((IsilRegisterOperand)firstRegister.Data).RegisterName[0] == 'W' ? 4 : 8;
×
157
                    builder.Move(instruction.Address, dest, firstRegister); // [REG + offset] = REG1
×
158
                    memory = new IsilMemoryOperand(memory.Base!.Value, memory.Addend + size);
×
159
                    dest = InstructionSetIndependentOperand.MakeMemory(memory);
×
160
                    builder.Move(instruction.Address, dest, ConvertOperand(instruction, 1)); // [REG + offset + size] = REG2
×
161
                }
162
                else // reg pointer
163
                {
164
                    var firstRegister = ConvertOperand(instruction, 0);
×
165
                    long size = ((IsilRegisterOperand)firstRegister.Data).RegisterName[0] == 'W' ? 4 : 8;
×
166
                    builder.Move(instruction.Address, dest, firstRegister);
×
167
                    builder.Add(instruction.Address, dest, dest, InstructionSetIndependentOperand.MakeImmediate(size));
×
168
                    builder.Move(instruction.Address, dest, ConvertOperand(instruction, 1));
×
169
                }
170
            }
171
                break;
×
172
            case Arm64Mnemonic.ADRP:
173
                //Just handle as a move
174
                // builder.Move(instruction.Address, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1));
175
                adrpOffsets[instruction.Op0Reg] = (ulong)instruction.Op1Imm;
×
176
                break;
×
177
            case Arm64Mnemonic.LDP when instruction.Op2Kind == Arm64OperandKind.Memory:
×
178
                //LDP (dest1, dest2, [mem]) - basically just treat as two loads, with the second offset by the length of the first
179
                var destRegSize = instruction.Op0Reg switch
×
180
                {
×
181
                    //vector (128 bit)
×
182
                    >= Arm64Register.V0 and <= Arm64Register.V31 => 16, //TODO check if this is accurate
×
183
                    //double
×
184
                    >= Arm64Register.D0 and <= Arm64Register.D31 => 8,
×
185
                    //single
×
186
                    >= Arm64Register.S0 and <= Arm64Register.S31 => 4,
×
187
                    //half
×
188
                    >= Arm64Register.H0 and <= Arm64Register.H31 => 2,
×
189
                    //word
×
190
                    >= Arm64Register.W0 and <= Arm64Register.W31 => 4,
×
191
                    //x
×
192
                    >= Arm64Register.X0 and <= Arm64Register.X31 => 8,
×
193
                    _ => throw new($"Unknown register size for LDP: {instruction.Op0Reg}")
×
194
                };
×
195

196
                var dest1 = ConvertOperand(instruction, 0);
×
197
                var dest2 = ConvertOperand(instruction, 1);
×
198
                var mem = ConvertOperand(instruction, 2);
×
199

200
                //TODO clean this mess up
201
                var memInternal = mem.Data as IsilMemoryOperand?;
×
202
                var mem2 = new IsilMemoryOperand(memInternal!.Value.Base!.Value, memInternal.Value.Addend + destRegSize);
×
203

204
                builder.Move(instruction.Address, dest1, mem);
×
205
                builder.Move(instruction.Address, dest2, InstructionSetIndependentOperand.MakeMemory(mem2));
×
206
                break;
×
207
            case Arm64Mnemonic.BL:
208
                builder.Call(instruction.Address, instruction.BranchTarget, GetArgumentOperandsForCall(context, instruction.BranchTarget).ToArray());
×
209
                break;
×
210
            case Arm64Mnemonic.RET:
211
                builder.Return(instruction.Address, GetReturnRegisterForContext(context));
×
212
                break;
×
213
            case Arm64Mnemonic.B:
214
                var target = instruction.BranchTarget;
×
215

216
                if (target < context.UnderlyingPointer || target > context.UnderlyingPointer + (ulong)context.RawBytes.Length)
×
217
                {
218
                    //Unconditional branch to outside the method, treat as call (tail-call, specifically) followed by return
219
                    builder.Call(instruction.Address, instruction.BranchTarget, GetArgumentOperandsForCall(context, instruction.BranchTarget).ToArray());
×
220
                    builder.Return(instruction.Address, GetReturnRegisterForContext(context));
×
221
                }
222

223
                break;
×
224
            case Arm64Mnemonic.BR:
225
                // branches unconditionally to an address in a register, with a hint that this is not a subroutine return.
226
                builder.CallRegister(instruction.Address, ConvertOperand(instruction, 0), noReturn: true);
×
227
                break;
×
228
            case Arm64Mnemonic.CBNZ:
229
            case Arm64Mnemonic.CBZ:
230
            {
231
                //Compare and branch if (non-)zero
232
                var targetAddr = (ulong)((long)instruction.Address + instruction.Op1Imm);
×
233

234
                //Compare to zero...
235
                builder.Compare(instruction.Address, ConvertOperand(instruction, 0), InstructionSetIndependentOperand.MakeImmediate(0));
×
236

237
                //And jump if (not) equal
238
                if (instruction.Mnemonic == Arm64Mnemonic.CBZ)
×
239
                    builder.JumpIfEqual(instruction.Address, targetAddr);
×
240
                else
241
                    builder.JumpIfNotEqual(instruction.Address, targetAddr);
×
242
            }
243
                break;
×
244

245
            case Arm64Mnemonic.CMP:
246
                // Compare: set flag (N or Z or C or V) = (reg1 - reg2)
247
                // builder.Compare(instruction.Address, ConvertOperand(instruction, 0), ConvertOperand(instruction, 0));
248
                goto default;
249

250
            case Arm64Mnemonic.TBNZ:
251
            // TBNZ R<t>, #imm, label
252
            // test bit and branch if NonZero
253
            case Arm64Mnemonic.TBZ:
254
                // TBZ R<t>, #imm, label
255
                // test bit and branch if Zero
256
            {
257
                var targetAddr = (ulong)((long)instruction.Address + instruction.Op2Imm);
×
258
                var bit = InstructionSetIndependentOperand.MakeImmediate(1 << (int)instruction.Op1Imm);
×
259
                var temp = InstructionSetIndependentOperand.MakeRegister("TEMP");
×
260
                var src = ConvertOperand(instruction, 0);
×
261
                builder.Move(instruction.Address, temp, src); // temp = src
×
262
                builder.And(instruction.Address, temp, temp, bit); // temp = temp & bit
×
263
                builder.Compare(instruction.Address, temp, bit); // result = temp == bit
×
264
                if (instruction.Mnemonic == Arm64Mnemonic.TBNZ)
×
265
                    builder.JumpIfEqual(instruction.Address, targetAddr); // if (result) goto targetAddr
×
266
                else
267
                    builder.JumpIfNotEqual(instruction.Address, targetAddr); // if (result) goto targetAddr
×
268
            }
269
                break;
×
270
            case Arm64Mnemonic.UBFM:
271
                // UBFM dest, src, #<immr>, #<imms>
272
                // dest = (src >> #<immr>) & ((1 << #<imms>) - 1)
273
            {
274
                var dest = ConvertOperand(instruction, 0);
×
275
                builder.Move(instruction.Address, dest, ConvertOperand(instruction, 1)); // dest = src
×
276
                builder.ShiftRight(instruction.Address, dest, ConvertOperand(instruction, 2)); // dest >> #<immr>
×
277
                var imms = (int)instruction.Op3Imm;
×
278
                builder.And(instruction.Address, dest, dest,
×
279
                    InstructionSetIndependentOperand.MakeImmediate((1 << imms) - 1)); // dest & constexpr { ((1 << #<imms>) - 1) }
×
280
            }
281
                break;
×
282

283
            case Arm64Mnemonic.MUL:
284
            case Arm64Mnemonic.FMUL:
285
                //Multiply is (dest, src1, src2)
286
                builder.Multiply(instruction.Address, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2));
×
287
                break;
×
288

289
            case Arm64Mnemonic.ADD:
290
            case Arm64Mnemonic.ADDS: // settings flags
291
            case Arm64Mnemonic.FADD:
292
                //Add is (dest, src1, src2)
293
                builder.Add(instruction.Address, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2));
×
294
                break;
×
295

296
            case Arm64Mnemonic.SUB:
297
            case Arm64Mnemonic.SUBS: // settings flags
298
            case Arm64Mnemonic.FSUB:
299
                //Sub is (dest, src1, src2)
300
                builder.Subtract(instruction.Address, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2));
×
301
                break;
×
302

303
            case Arm64Mnemonic.AND:
304
            case Arm64Mnemonic.ANDS:
305
                //And is (dest, src1, src2)
306
                builder.And(instruction.Address, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2));
×
307
                break;
×
308

309
            case Arm64Mnemonic.ORR:
310
                //Orr is (dest, src1, src2)
311
                builder.Or(instruction.Address, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2));
×
312
                break;
×
313

314
            case Arm64Mnemonic.EOR:
315
                //Eor (aka xor) is (dest, src1, src2)
316
                builder.Xor(instruction.Address, ConvertOperand(instruction, 0), ConvertOperand(instruction, 1), ConvertOperand(instruction, 2));
×
317
                break;
×
318

319
            default:
320
                builder.NotImplemented(instruction.Address, $"Instruction {instruction.Mnemonic} not yet implemented.");
×
321
                break;
322
        }
323
    }
×
324

325
    private InstructionSetIndependentOperand ConvertOperand(Arm64Instruction instruction, int operand)
326
    {
327
        var kind = operand switch
×
328
        {
×
329
            0 => instruction.Op0Kind,
×
330
            1 => instruction.Op1Kind,
×
331
            2 => instruction.Op2Kind,
×
332
            3 => instruction.Op3Kind,
×
333
            _ => throw new ArgumentOutOfRangeException(nameof(operand), $"Operand must be between 0 and 3, inclusive. Got {operand}")
×
334
        };
×
335

336
        if (kind is Arm64OperandKind.Immediate or Arm64OperandKind.ImmediatePcRelative)
×
337
        {
338
            var imm = operand switch
×
339
            {
×
340
                0 => instruction.Op0Imm,
×
341
                1 => instruction.Op1Imm,
×
342
                2 => instruction.Op2Imm,
×
343
                3 => instruction.Op3Imm,
×
344
                _ => throw new ArgumentOutOfRangeException(nameof(operand), $"Operand must be between 0 and 3, inclusive. Got {operand}")
×
345
            };
×
346

347
            if (kind == Arm64OperandKind.ImmediatePcRelative)
×
348
                imm += (long)instruction.Address + 4; //Add 4 to the address to get the address of the next instruction (PC-relative addressing is relative to the address of the next instruction, not the current one
×
349

350
            return InstructionSetIndependentOperand.MakeImmediate(imm);
×
351
        }
352

353
        if (kind == Arm64OperandKind.FloatingPointImmediate)
×
354
        {
355
            var imm = operand switch
×
356
            {
×
357
                0 => instruction.Op0FpImm,
×
358
                1 => instruction.Op1FpImm,
×
359
                2 => instruction.Op2FpImm,
×
360
                3 => instruction.Op3FpImm,
×
361
                _ => throw new ArgumentOutOfRangeException(nameof(operand), $"Operand must be between 0 and 3, inclusive. Got {operand}")
×
362
            };
×
363

364
            return InstructionSetIndependentOperand.MakeImmediate(imm);
×
365
        }
366

367
        if (kind == Arm64OperandKind.Register)
×
368
        {
369
            var reg = operand switch
×
370
            {
×
371
                0 => instruction.Op0Reg,
×
372
                1 => instruction.Op1Reg,
×
373
                2 => instruction.Op2Reg,
×
374
                3 => instruction.Op3Reg,
×
375
                _ => throw new ArgumentOutOfRangeException(nameof(operand), $"Operand must be between 0 and 3, inclusive. Got {operand}")
×
376
            };
×
377

378
            return InstructionSetIndependentOperand.MakeRegister(reg.ToString().ToUpperInvariant());
×
379
        }
380

381
        if (kind == Arm64OperandKind.Memory)
×
382
        {
383
            var reg = instruction.MemBase;
×
384
            var offset = instruction.MemOffset;
×
385
            var isPreIndexed = instruction.MemIsPreIndexed;
×
386

387
            if (reg == Arm64Register.INVALID)
×
388
                //Offset only
389
                return InstructionSetIndependentOperand.MakeMemory(new IsilMemoryOperand(offset));
×
390

391
            //TODO Handle more stuff here
392
            return InstructionSetIndependentOperand.MakeMemory(new IsilMemoryOperand(
×
393
                InstructionSetIndependentOperand.MakeRegister(reg.ToString().ToUpperInvariant()),
×
394
                offset));
×
395
        }
396

397
        if (kind == Arm64OperandKind.VectorRegisterElement)
×
398
        {
399
            var reg = operand switch
×
400
            {
×
401
                0 => instruction.Op0Reg,
×
402
                1 => instruction.Op1Reg,
×
403
                2 => instruction.Op2Reg,
×
404
                3 => instruction.Op3Reg,
×
405
                _ => throw new ArgumentOutOfRangeException(nameof(operand), $"Operand must be between 0 and 3, inclusive. Got {operand}")
×
406
            };
×
407

408
            var vectorElement = operand switch
×
409
            {
×
410
                0 => instruction.Op0VectorElement,
×
411
                1 => instruction.Op1VectorElement,
×
412
                2 => instruction.Op2VectorElement,
×
413
                3 => instruction.Op3VectorElement,
×
414
                _ => throw new ArgumentOutOfRangeException(nameof(operand), $"Operand must be between 0 and 3, inclusive. Got {operand}")
×
415
            };
×
416

417
            var width = vectorElement.Width switch
×
418
            {
×
419
                Arm64VectorElementWidth.B => IsilVectorRegisterElementOperand.VectorElementWidth.B,
×
420
                Arm64VectorElementWidth.H => IsilVectorRegisterElementOperand.VectorElementWidth.H,
×
421
                Arm64VectorElementWidth.S => IsilVectorRegisterElementOperand.VectorElementWidth.S,
×
422
                Arm64VectorElementWidth.D => IsilVectorRegisterElementOperand.VectorElementWidth.D,
×
423
                _ => throw new ArgumentOutOfRangeException(nameof(vectorElement.Width), $"Unknown vector element width {vectorElement.Width}")
×
424
            };
×
425

426
            //<Reg>.<Width>[<Index>]
427
            return InstructionSetIndependentOperand.MakeVectorElement(reg.ToString().ToUpperInvariant(), width, vectorElement.Index);
×
428
        }
429

430
        return InstructionSetIndependentOperand.MakeImmediate($"<UNIMPLEMENTED OPERAND TYPE {kind}>");
×
431
    }
432

433
    public override BaseKeyFunctionAddresses CreateKeyFunctionAddressesInstance() => new NewArm64KeyFunctionAddresses();
×
434

435
    public override string PrintAssembly(MethodAnalysisContext context) => context.RawBytes.Span.Length <= 0 ? "" : string.Join("\n", Disassembler.Disassemble(context.RawBytes.Span, context.UnderlyingPointer, new Disassembler.Options(true, true, false)).ToList());
×
436

437
    private InstructionSetIndependentOperand? GetReturnRegisterForContext(MethodAnalysisContext context)
438
    {
NEW
439
        var returnType = context.ReturnType;
×
440
        if (returnType.Namespace == nameof(System))
×
441
        {
442
            return returnType.Name switch
×
443
            {
×
444
                "Void" => null, //Void is no return
×
445
                "Double" => InstructionSetIndependentOperand.MakeRegister(nameof(Arm64Register.V0)), //Builtin double is v0
×
446
                "Single" => InstructionSetIndependentOperand.MakeRegister(nameof(Arm64Register.V0)), //Builtin float is v0
×
447
                _ => InstructionSetIndependentOperand.MakeRegister(nameof(Arm64Register.X0)), //All other system types are x0 like any other pointer
×
448
            };
×
449
        }
450

451
        //TODO Do certain value types have different return registers?
452

453
        //Any user type is returned in x0
454
        return InstructionSetIndependentOperand.MakeRegister(nameof(Arm64Register.X0));
×
455
    }
456

457
    private List<InstructionSetIndependentOperand> GetArgumentOperandsForCall(MethodAnalysisContext contextBeingAnalyzed, ulong callAddr)
458
    {
459
        if (!contextBeingAnalyzed.AppContext.MethodsByAddress.TryGetValue(callAddr, out var methodsAtAddress))
×
460
            //TODO
461
            return [];
×
462

463
        //For the sake of arguments, all we care about is the first method at the address, because they'll only be shared if they have the same signature.
464
        var contextBeingCalled = methodsAtAddress.First();
×
465

466
        var vectorCount = 0;
×
467
        var nonVectorCount = 0;
×
468

469
        var ret = new List<InstructionSetIndependentOperand>();
×
470

471
        //Handle 'this' if it's an instance method
472
        if (!contextBeingCalled.IsStatic)
×
473
        {
474
            ret.Add(InstructionSetIndependentOperand.MakeRegister(nameof(Arm64Register.X0)));
×
475
            nonVectorCount++;
×
476
        }
477

478
        foreach (var parameter in contextBeingCalled.Parameters)
×
479
        {
NEW
480
            var paramType = parameter.ParameterType;
×
481
            if (paramType.Namespace == nameof(System))
×
482
            {
483
                switch (paramType.Name)
×
484
                {
485
                    case "Single":
486
                    case "Double":
487
                        ret.Add(InstructionSetIndependentOperand.MakeRegister((Arm64Register.V0 + vectorCount++).ToString().ToUpperInvariant()));
×
488
                        break;
×
489
                    default:
490
                        ret.Add(InstructionSetIndependentOperand.MakeRegister((Arm64Register.X0 + nonVectorCount++).ToString().ToUpperInvariant()));
×
491
                        break;
×
492
                }
493
            }
494
            else
495
            {
496
                ret.Add(InstructionSetIndependentOperand.MakeRegister((Arm64Register.X0 + nonVectorCount++).ToString().ToUpperInvariant()));
×
497
            }
498
        }
499

500
        return ret;
×
501
    }
502
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc