From 892df335e6f633a5bcc2ce265749e15365ea2549 Mon Sep 17 00:00:00 2001 From: Thog Date: Mon, 13 Jan 2020 03:04:28 +0100 Subject: Rework SVC handling (#883) * Rework SVC handling Prepare for 32 bits support. * QueryMemory64 x1 is an output * Pregenerate all SVC handler Also clean up + 32 bits code path * Address gdk's comments * Simplify local setter loop * Address jd's comments --- Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs | 105 ++++++++++++++-------- 1 file changed, 68 insertions(+), 37 deletions(-) (limited to 'Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs') diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs index 1c2121f0..27aa70fc 100644 --- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/SvcTable.cs @@ -3,6 +3,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Common; using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -10,15 +11,19 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { static class SvcTable { - private const int SvcFuncMaxArguments = 8; + private const int SvcFuncMaxArguments64 = 8; + private const int SvcFuncMaxArguments32 = 4; + private const int SvcMax = 0x80; - private static Dictionary _svcFuncs64; - - private static Action[] _svcTable64; + public static Action[] SvcTable32 { get; } + public static Action[] SvcTable64 { get; } static SvcTable() { - _svcFuncs64 = new Dictionary + SvcTable32 = new Action[SvcMax]; + SvcTable64 = new Action[SvcMax]; + + Dictionary svcFuncs64 = new Dictionary { { 0x01, nameof(SvcHandler.SetHeapSize64) }, { 0x03, nameof(SvcHandler.SetMemoryAttribute64) }, @@ -78,25 +83,23 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall { 0x7B, nameof(SvcHandler.TerminateProcess64) } }; - _svcTable64 = new Action[0x80]; - } - - public static Action GetSvcFunc(int svcId) - { - if (_svcTable64[svcId] != null) + foreach (KeyValuePair value in svcFuncs64) { - return _svcTable64[svcId]; + SvcTable64[value.Key] = GenerateMethod(value.Value, SvcFuncMaxArguments64); } - if (_svcFuncs64.TryGetValue(svcId, out string svcName)) + Dictionary svcFuncs32 = new Dictionary { - return _svcTable64[svcId] = GenerateMethod(svcName); - } + // TODO + }; - return null; + foreach (KeyValuePair value in svcFuncs32) + { + SvcTable32[value.Key] = GenerateMethod(value.Value, SvcFuncMaxArguments32); + } } - private static Action GenerateMethod(string svcName) + private static Action GenerateMethod(string svcName, int registerCleanCount) { Type[] argTypes = new Type[] { typeof(SvcHandler), typeof(ExecutionContext) }; @@ -106,11 +109,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall ParameterInfo[] methodArgs = methodInfo.GetParameters(); - if (methodArgs.Length > SvcFuncMaxArguments) - { - throw new InvalidOperationException($"Method \"{svcName}\" has too many arguments, max is 8."); - } - ILGenerator generator = method.GetILGenerator(); void ConvertToArgType(Type sourceType) @@ -152,6 +150,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall } } + RAttribute GetRegisterAttribute(ParameterInfo parameterInfo) + { + RAttribute argumentAttribute = (RAttribute)parameterInfo.GetCustomAttribute(typeof(RAttribute)); + + if (argumentAttribute == null) + { + throw new InvalidOperationException($"Method \"{svcName}\" is missing a {typeof(RAttribute).Name} attribute on parameter \"{parameterInfo.Name}\""); + } + + return argumentAttribute; + } + // For functions returning output values, the first registers // are used to hold pointers where the value will be stored, // so they can't be used to pass argument and we must @@ -179,15 +189,25 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall string argsFormat = svcName; - for (int index = 0; index < inputArgsCount; index++) + for (int index = 0; index < methodArgs.Length; index++) { + Type argType = methodArgs[index].ParameterType; + + // Ignore out argument for printing + if (argType.IsByRef) + { + continue; + } + + RAttribute registerAttribute = GetRegisterAttribute(methodArgs[index]); + argsFormat += $" {methodArgs[index].Name}: 0x{{{index}:X8}},"; generator.Emit(OpCodes.Dup); generator.Emit(OpCodes.Ldc_I4, index); generator.Emit(OpCodes.Ldarg_1); - generator.Emit(OpCodes.Ldc_I4, byRefArgsCount + index); + generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); @@ -200,7 +220,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall argsFormat = argsFormat.Substring(0, argsFormat.Length - 1); - generator.Emit(OpCodes.Ldstr, argsFormat); + generator.Emit(OpCodes.Ldstr, argsFormat); } else { @@ -216,11 +236,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall // Call the SVC function handler. generator.Emit(OpCodes.Ldarg_0); - List locals = new List(); + List<(LocalBuilder, RAttribute)> locals = new List<(LocalBuilder, RAttribute)>(); for (int index = 0; index < methodArgs.Length; index++) { Type argType = methodArgs[index].ParameterType; + RAttribute registerAttribute = GetRegisterAttribute(methodArgs[index]); if (argType.IsByRef) { @@ -228,12 +249,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall LocalBuilder local = generator.DeclareLocal(argType); - locals.Add(local); + locals.Add((local, registerAttribute)); if (!methodArgs[index].IsOut) { generator.Emit(OpCodes.Ldarg_1); - generator.Emit(OpCodes.Ldc_I4, index); + generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); @@ -249,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall else { generator.Emit(OpCodes.Ldarg_1); - generator.Emit(OpCodes.Ldc_I4, byRefArgsCount + index); + generator.Emit(OpCodes.Ldc_I4, registerAttribute.Index); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.GetX)); @@ -261,8 +282,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall generator.Emit(OpCodes.Call, methodInfo); - int outRegIndex = 0; - Type retType = methodInfo.ReturnType; // Print result code. @@ -275,6 +294,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall generator.Emit(OpCodes.Call, printResultMethod); } + uint registerInUse = 0; + // Save return value into register X0 (when the method has a return value). if (retType != typeof(void)) { @@ -284,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall generator.Emit(OpCodes.Stloc, tempLocal); generator.Emit(OpCodes.Ldarg_1); - generator.Emit(OpCodes.Ldc_I4, outRegIndex++); + generator.Emit(OpCodes.Ldc_I4, 0); generator.Emit(OpCodes.Ldloc, tempLocal); ConvertToFieldType(retType); @@ -292,26 +313,36 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX)); generator.Emit(OpCodes.Call, info); + + registerInUse |= 1u << 0; } for (int index = 0; index < locals.Count; index++) { + (LocalBuilder local, RAttribute attribute) = locals[index]; generator.Emit(OpCodes.Ldarg_1); - generator.Emit(OpCodes.Ldc_I4, outRegIndex++); - generator.Emit(OpCodes.Ldloc, locals[index]); + generator.Emit(OpCodes.Ldc_I4, attribute.Index); + generator.Emit(OpCodes.Ldloc, local); - ConvertToFieldType(locals[index].LocalType); + ConvertToFieldType(local.LocalType); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX)); generator.Emit(OpCodes.Call, info); + + registerInUse |= 1u << attribute.Index; } // Zero out the remaining unused registers. - while (outRegIndex < SvcFuncMaxArguments) + for (int i = 0; i < registerCleanCount; i++) { + if ((registerInUse & (1u << i)) != 0) + { + continue; + } + generator.Emit(OpCodes.Ldarg_1); - generator.Emit(OpCodes.Ldc_I4, outRegIndex++); + generator.Emit(OpCodes.Ldc_I4, i); generator.Emit(OpCodes.Ldc_I8, 0L); MethodInfo info = typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.SetX)); -- cgit v1.2.3