aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/Translation/DirectCallStubs.cs
blob: e6e87b2b6197d595c81c98b0139091ca5f80bdfc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using System;
using System.Runtime.InteropServices;

using static ARMeilleure.IntermediateRepresentation.OperandHelper;

namespace ARMeilleure.Translation
{
    static class DirectCallStubs
    {
        private delegate long GuestFunction(IntPtr nativeContextPtr);

        private static GuestFunction _directCallStub;
        private static GuestFunction _directTailCallStub;
        private static GuestFunction _indirectCallStub;
        private static GuestFunction _indirectTailCallStub;

        private static object _lock;
        private static bool _initialized;

        static DirectCallStubs()
        {
            _lock = new object();
        }

        public static void InitializeStubs()
        {
            if (_initialized) return;
            lock (_lock)
            {
                if (_initialized) return;
                _directCallStub = GenerateDirectCallStub(false);
                _directTailCallStub = GenerateDirectCallStub(true);
                _indirectCallStub = GenerateIndirectCallStub(false);
                _indirectTailCallStub = GenerateIndirectCallStub(true);
                _initialized = true;
            }
        }

        public static IntPtr DirectCallStub(bool tailCall)
        {
            return Marshal.GetFunctionPointerForDelegate(tailCall ? _directTailCallStub : _directCallStub);
        }

        public static IntPtr IndirectCallStub(bool tailCall)
        {
            return Marshal.GetFunctionPointerForDelegate(tailCall ? _indirectTailCallStub : _indirectCallStub);
        }

        private static void EmitCall(EmitterContext context, Operand address, bool tailCall)
        {
            if (tailCall)
            {
                context.Tailcall(address, context.LoadArgument(OperandType.I64, 0));
            }
            else
            {
                context.Return(context.Call(address, OperandType.I64, context.LoadArgument(OperandType.I64, 0)));
            }
        }

        /// <summary>
        /// Generates a stub that is used to find function addresses. Used for direct calls when their jump table does not have the host address yet.
        /// Takes a NativeContext like a translated guest function, and extracts the target address from the NativeContext.
        /// When the target function is compiled in highCq, all table entries are updated to point to that function instead of this stub by the translator.
        /// </summary>
        private static GuestFunction GenerateDirectCallStub(bool tailCall)
        {
            EmitterContext context = new EmitterContext();

            Operand nativeContextPtr = context.LoadArgument(OperandType.I64, 0);

            Operand address = context.Load(OperandType.I64, context.Add(nativeContextPtr, Const((long)NativeContext.GetCallAddressOffset())));

            address = context.BitwiseOr(address, Const(address.Type, 1)); // Set call flag.
            Operand functionAddr = context.Call(new _U64_U64(NativeInterface.GetFunctionAddress), address);
            EmitCall(context, functionAddr, tailCall);

            ControlFlowGraph cfg = context.GetControlFlowGraph();

            OperandType[] argTypes = new OperandType[]
            {
                OperandType.I64
            };

            return Compiler.Compile<GuestFunction>(
                cfg,
                argTypes,
                OperandType.I64,
                CompilerOptions.HighCq);
        }

        /// <summary>
        /// Generates a stub that is used to find function addresses and add them to an indirect table. 
        /// Used for indirect calls entries (already claimed) when their jump table does not have the host address yet.
        /// Takes a NativeContext like a translated guest function, and extracts the target indirect table entry from the NativeContext.
        /// If the function we find is highCq, the entry in the table is updated to point to that function rather than this stub.
        /// </summary>
        private static GuestFunction GenerateIndirectCallStub(bool tailCall)
        {
            EmitterContext context = new EmitterContext();

            Operand nativeContextPtr = context.LoadArgument(OperandType.I64, 0);

            Operand entryAddress = context.Load(OperandType.I64, context.Add(nativeContextPtr, Const((long)NativeContext.GetCallAddressOffset())));
            Operand address = context.Load(OperandType.I64, entryAddress);

            // We need to find the missing function. If the function is HighCq, then it replaces this stub in the indirect table.
            // Either way, we call it afterwards.
            Operand functionAddr = context.Call(new _U64_U64_U64(NativeInterface.GetIndirectFunctionAddress), address, entryAddress);

            // Call and save the function.
            EmitCall(context, functionAddr, tailCall);

            ControlFlowGraph cfg = context.GetControlFlowGraph();

            OperandType[] argTypes = new OperandType[]
            {
                OperandType.I64
            };

            return Compiler.Compile<GuestFunction>(
                cfg,
                argTypes,
                OperandType.I64,
                CompilerOptions.HighCq);
        }
    }
}