| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- using Microsoft.CodeAnalysis;
- using Microsoft.CodeAnalysis.CSharp;
- using Microsoft.CodeAnalysis.CSharp.Syntax;
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- namespace Ryujinx.Horizon.Generators.Kernel
- {
- [Generator]
- class SyscallGenerator : ISourceGenerator
- {
- private const string ClassNamespace = "Ryujinx.HLE.HOS.Kernel.SupervisorCall";
- private const string ClassName = "SyscallDispatch";
- private const string A32Suffix = "32";
- private const string A64Suffix = "64";
- private const string ResultVariableName = "result";
- private const string ArgVariablePrefix = "arg";
- private const string ResultCheckHelperName = "LogResultAsTrace";
- private const string TypeSystemBoolean = "System.Boolean";
- private const string TypeSystemInt32 = "System.Int32";
- private const string TypeSystemInt64 = "System.Int64";
- private const string TypeSystemUInt32 = "System.UInt32";
- private const string TypeSystemUInt64 = "System.UInt64";
- private const string NamespaceKernel = "Ryujinx.HLE.HOS.Kernel";
- private const string NamespaceHorizonCommon = "Ryujinx.Horizon.Common";
- private const string TypeSvcAttribute = NamespaceKernel + ".SupervisorCall.SvcAttribute";
- private const string TypePointerSizedAttribute = NamespaceKernel + ".SupervisorCall.PointerSizedAttribute";
- private const string TypeResultName = "Result";
- private const string TypeKernelResultName = "KernelResult";
- private const string TypeResult = NamespaceHorizonCommon + "." + TypeResultName;
- private const string TypeExecutionContext = "IExecutionContext";
- private static readonly string[] _expectedResults = new string[]
- {
- $"{TypeResultName}.Success",
- $"{TypeKernelResultName}.TimedOut",
- $"{TypeKernelResultName}.Cancelled",
- $"{TypeKernelResultName}.PortRemoteClosed",
- $"{TypeKernelResultName}.InvalidState"
- };
- private readonly struct OutParameter
- {
- public readonly string Identifier;
- public readonly bool NeedsSplit;
- public OutParameter(string identifier, bool needsSplit = false)
- {
- Identifier = identifier;
- NeedsSplit = needsSplit;
- }
- }
- private struct RegisterAllocatorA32
- {
- private uint _useSet;
- private int _linearIndex;
- public int AllocateSingle()
- {
- return Allocate();
- }
- public (int, int) AllocatePair()
- {
- _linearIndex += _linearIndex & 1;
- return (Allocate(), Allocate());
- }
- private int Allocate()
- {
- int regIndex;
- if (_linearIndex < 4)
- {
- regIndex = _linearIndex++;
- }
- else
- {
- regIndex = -1;
- for (int i = 0; i < 32; i++)
- {
- if ((_useSet & (1 << i)) == 0)
- {
- regIndex = i;
- break;
- }
- }
- Debug.Assert(regIndex != -1);
- }
- _useSet |= 1u << regIndex;
- return regIndex;
- }
- public void AdvanceLinearIndex()
- {
- _linearIndex++;
- }
- }
- private readonly struct SyscallIdAndName : IComparable<SyscallIdAndName>
- {
- public readonly int Id;
- public readonly string Name;
- public SyscallIdAndName(int id, string name)
- {
- Id = id;
- Name = name;
- }
- public int CompareTo(SyscallIdAndName other)
- {
- return Id.CompareTo(other.Id);
- }
- }
- public void Execute(GeneratorExecutionContext context)
- {
- SyscallSyntaxReceiver syntaxReceiver = (SyscallSyntaxReceiver)context.SyntaxReceiver;
- CodeGenerator generator = new CodeGenerator();
- generator.AppendLine("using Ryujinx.Common.Logging;");
- generator.AppendLine("using Ryujinx.Cpu;");
- generator.AppendLine($"using {NamespaceKernel}.Common;");
- generator.AppendLine($"using {NamespaceKernel}.Memory;");
- generator.AppendLine($"using {NamespaceKernel}.Process;");
- generator.AppendLine($"using {NamespaceKernel}.Threading;");
- generator.AppendLine($"using {NamespaceHorizonCommon};");
- generator.AppendLine("using System;");
- generator.AppendLine();
- generator.EnterScope($"namespace {ClassNamespace}");
- generator.EnterScope($"static class {ClassName}");
- GenerateResultCheckHelper(generator);
- generator.AppendLine();
- List<SyscallIdAndName> syscalls = new List<SyscallIdAndName>();
- foreach (var method in syntaxReceiver.SvcImplementations)
- {
- GenerateMethod32(generator, context.Compilation, method);
- GenerateMethod64(generator, context.Compilation, method);
- foreach (AttributeSyntax attribute in method.AttributeLists.SelectMany(attributeList =>
- attributeList.Attributes.Where(attribute =>
- GetCanonicalTypeName(context.Compilation, attribute) == TypeSvcAttribute)))
- {
- syscalls.AddRange(from attributeArg in attribute.ArgumentList.Arguments
- where attributeArg.Expression.Kind() == SyntaxKind.NumericLiteralExpression
- select (LiteralExpressionSyntax)attributeArg.Expression
- into numericLiteral
- select new SyscallIdAndName((int)numericLiteral.Token.Value, method.Identifier.Text));
- }
- }
- syscalls.Sort();
- GenerateDispatch(generator, syscalls, A32Suffix);
- generator.AppendLine();
- GenerateDispatch(generator, syscalls, A64Suffix);
- generator.LeaveScope();
- generator.LeaveScope();
- context.AddSource($"{ClassName}.g.cs", generator.ToString());
- }
- private static void GenerateResultCheckHelper(CodeGenerator generator)
- {
- generator.EnterScope($"private static bool {ResultCheckHelperName}({TypeResultName} {ResultVariableName})");
- string[] expectedChecks = new string[_expectedResults.Length];
- for (int i = 0; i < expectedChecks.Length; i++)
- {
- expectedChecks[i] = $"{ResultVariableName} == {_expectedResults[i]}";
- }
- string checks = string.Join(" || ", expectedChecks);
- generator.AppendLine($"return {checks};");
- generator.LeaveScope();
- }
- private static void GenerateMethod32(CodeGenerator generator, Compilation compilation, MethodDeclarationSyntax method)
- {
- generator.EnterScope($"private static void {method.Identifier.Text}{A32Suffix}(Syscall syscall, {TypeExecutionContext} context)");
- string[] args = new string[method.ParameterList.Parameters.Count];
- int index = 0;
- RegisterAllocatorA32 regAlloc = new RegisterAllocatorA32();
- List<OutParameter> outParameters = new List<OutParameter>();
- List<string> logInArgs = new List<string>();
- List<string> logOutArgs = new List<string>();
- foreach (var methodParameter in method.ParameterList.Parameters)
- {
- string name = methodParameter.Identifier.Text;
- string argName = GetPrefixedArgName(name);
- string typeName = methodParameter.Type.ToString();
- string canonicalTypeName = GetCanonicalTypeName(compilation, methodParameter.Type);
- if (methodParameter.Modifiers.Any(SyntaxKind.OutKeyword))
- {
- bool needsSplit = Is64BitInteger(canonicalTypeName) && !IsPointerSized(compilation, methodParameter);
- outParameters.Add(new OutParameter(argName, needsSplit));
- logOutArgs.Add($"{name}: {GetFormattedLogValue(argName, canonicalTypeName)}");
- argName = $"out {typeName} {argName}";
- regAlloc.AdvanceLinearIndex();
- }
- else
- {
- if (Is64BitInteger(canonicalTypeName))
- {
- if (IsPointerSized(compilation, methodParameter))
- {
- int registerIndex = regAlloc.AllocateSingle();
- generator.AppendLine($"var {argName} = (uint)context.GetX({registerIndex});");
- }
- else
- {
- (int registerIndex, int registerIndex2) = regAlloc.AllocatePair();
- string valueLow = $"(ulong)(uint)context.GetX({registerIndex})";
- string valueHigh = $"(ulong)(uint)context.GetX({registerIndex2})";
- string value = $"{valueLow} | ({valueHigh} << 32)";
- generator.AppendLine($"var {argName} = ({typeName})({value});");
- }
- }
- else
- {
- int registerIndex = regAlloc.AllocateSingle();
- string value = GenerateCastFromUInt64($"context.GetX({registerIndex})", canonicalTypeName, typeName);
- generator.AppendLine($"var {argName} = {value};");
- }
- logInArgs.Add($"{name}: {GetFormattedLogValue(argName, canonicalTypeName)}");
- }
- args[index++] = argName;
- }
- GenerateLogPrintBeforeCall(generator, method.Identifier.Text, logInArgs);
- string argsList = string.Join(", ", args);
- int returnRegisterIndex = 0;
- string result = null;
- string canonicalReturnTypeName = null;
- if (method.ReturnType.ToString() != "void")
- {
- generator.AppendLine($"var {ResultVariableName} = syscall.{method.Identifier.Text}({argsList});");
- canonicalReturnTypeName = GetCanonicalTypeName(compilation, method.ReturnType);
- if (canonicalReturnTypeName == TypeResult)
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint){ResultVariableName}.ErrorCode);");
- }
- else
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint){ResultVariableName});");
- }
- if (Is64BitInteger(canonicalReturnTypeName))
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint)({ResultVariableName} >> 32));");
- }
- result = GetFormattedLogValue(ResultVariableName, canonicalReturnTypeName);
- }
- else
- {
- generator.AppendLine($"syscall.{method.Identifier.Text}({argsList});");
- }
- foreach (OutParameter outParameter in outParameters)
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint){outParameter.Identifier});");
- if (outParameter.NeedsSplit)
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, (uint)({outParameter.Identifier} >> 32));");
- }
- }
- while (returnRegisterIndex < 4)
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, 0);");
- }
- GenerateLogPrintAfterCall(generator, method.Identifier.Text, logOutArgs, result, canonicalReturnTypeName);
- generator.LeaveScope();
- generator.AppendLine();
- }
- private static void GenerateMethod64(CodeGenerator generator, Compilation compilation, MethodDeclarationSyntax method)
- {
- generator.EnterScope($"private static void {method.Identifier.Text}{A64Suffix}(Syscall syscall, {TypeExecutionContext} context)");
- string[] args = new string[method.ParameterList.Parameters.Count];
- int registerIndex = 0;
- int index = 0;
- List<OutParameter> outParameters = new List<OutParameter>();
- List<string> logInArgs = new List<string>();
- List<string> logOutArgs = new List<string>();
- foreach (var methodParameter in method.ParameterList.Parameters)
- {
- string name = methodParameter.Identifier.Text;
- string argName = GetPrefixedArgName(name);
- string typeName = methodParameter.Type.ToString();
- string canonicalTypeName = GetCanonicalTypeName(compilation, methodParameter.Type);
- if (methodParameter.Modifiers.Any(SyntaxKind.OutKeyword))
- {
- outParameters.Add(new OutParameter(argName));
- logOutArgs.Add($"{name}: {GetFormattedLogValue(argName, canonicalTypeName)}");
- argName = $"out {typeName} {argName}";
- registerIndex++;
- }
- else
- {
- string value = GenerateCastFromUInt64($"context.GetX({registerIndex++})", canonicalTypeName, typeName);
- generator.AppendLine($"var {argName} = {value};");
- logInArgs.Add($"{name}: {GetFormattedLogValue(argName, canonicalTypeName)}");
- }
- args[index++] = argName;
- }
- GenerateLogPrintBeforeCall(generator, method.Identifier.Text, logInArgs);
- string argsList = string.Join(", ", args);
- int returnRegisterIndex = 0;
- string result = null;
- string canonicalReturnTypeName = null;
- if (method.ReturnType.ToString() != "void")
- {
- generator.AppendLine($"var {ResultVariableName} = syscall.{method.Identifier.Text}({argsList});");
- canonicalReturnTypeName = GetCanonicalTypeName(compilation, method.ReturnType);
- if (canonicalReturnTypeName == TypeResult)
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, (ulong){ResultVariableName}.ErrorCode);");
- }
- else
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, (ulong){ResultVariableName});");
- }
-
- result = GetFormattedLogValue(ResultVariableName, canonicalReturnTypeName);
- }
- else
- {
- generator.AppendLine($"syscall.{method.Identifier.Text}({argsList});");
- }
- foreach (OutParameter outParameter in outParameters)
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, (ulong){outParameter.Identifier});");
- }
- while (returnRegisterIndex < 8)
- {
- generator.AppendLine($"context.SetX({returnRegisterIndex++}, 0);");
- }
- GenerateLogPrintAfterCall(generator, method.Identifier.Text, logOutArgs, result, canonicalReturnTypeName);
- generator.LeaveScope();
- generator.AppendLine();
- }
- private static string GetFormattedLogValue(string value, string canonicalTypeName)
- {
- if (Is32BitInteger(canonicalTypeName))
- {
- return $"0x{{{value}:X8}}";
- }
- else if (Is64BitInteger(canonicalTypeName))
- {
- return $"0x{{{value}:X16}}";
- }
- return $"{{{value}}}";
- }
- private static string GetPrefixedArgName(string name)
- {
- return ArgVariablePrefix + char.ToUpperInvariant(name[0]) + name.Substring(1);
- }
- private static string GetCanonicalTypeName(Compilation compilation, SyntaxNode syntaxNode)
- {
- TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode);
- if (typeInfo.Type.ContainingNamespace == null)
- {
- return typeInfo.Type.Name;
- }
- return $"{typeInfo.Type.ContainingNamespace.ToDisplayString()}.{typeInfo.Type.Name}";
- }
- private static void GenerateLogPrintBeforeCall(CodeGenerator generator, string methodName, List<string> argList)
- {
- string log = $"{methodName}({string.Join(", ", argList)})";
- GenerateLogPrint(generator, "Trace", "KernelSvc", log);
- }
- private static void GenerateLogPrintAfterCall(
- CodeGenerator generator,
- string methodName,
- List<string> argList,
- string result,
- string canonicalResultTypeName)
- {
- string log = $"{methodName}({string.Join(", ", argList)})";
- if (result != null)
- {
- log += $" = {result}";
- }
- if (canonicalResultTypeName == TypeResult)
- {
- generator.EnterScope($"if ({ResultCheckHelperName}({ResultVariableName}))");
- GenerateLogPrint(generator, "Trace", "KernelSvc", log);
- generator.LeaveScope();
- generator.EnterScope("else");
- GenerateLogPrint(generator, "Warning", "KernelSvc", log);
- generator.LeaveScope();
- }
- else
- {
- GenerateLogPrint(generator, "Trace", "KernelSvc", log);
- }
- }
- private static void GenerateLogPrint(CodeGenerator generator, string logLevel, string logClass, string log)
- {
- generator.AppendLine($"Logger.{logLevel}?.PrintMsg(LogClass.{logClass}, $\"{log}\");");
- }
- private static void GenerateDispatch(CodeGenerator generator, List<SyscallIdAndName> syscalls, string suffix)
- {
- generator.EnterScope($"public static void Dispatch{suffix}(Syscall syscall, {TypeExecutionContext} context, int id)");
- generator.EnterScope("switch (id)");
- foreach (var syscall in syscalls)
- {
- generator.AppendLine($"case {syscall.Id}:");
- generator.IncreaseIndentation();
- generator.AppendLine($"{syscall.Name}{suffix}(syscall, context);");
- generator.AppendLine("break;");
- generator.DecreaseIndentation();
- }
- generator.AppendLine($"default:");
- generator.IncreaseIndentation();
- generator.AppendLine("throw new NotImplementedException($\"SVC 0x{id:X4} is not implemented.\");");
- generator.DecreaseIndentation();
- generator.LeaveScope();
- generator.LeaveScope();
- }
- private static bool Is32BitInteger(string canonicalTypeName)
- {
- return canonicalTypeName == TypeSystemInt32 || canonicalTypeName == TypeSystemUInt32;
- }
- private static bool Is64BitInteger(string canonicalTypeName)
- {
- return canonicalTypeName == TypeSystemInt64 || canonicalTypeName == TypeSystemUInt64;
- }
- private static string GenerateCastFromUInt64(string value, string canonicalTargetTypeName, string targetTypeName)
- {
- return canonicalTargetTypeName == TypeSystemBoolean ? $"({value} & 1) != 0" : $"({targetTypeName}){value}";
- }
- private static bool IsPointerSized(Compilation compilation, ParameterSyntax parameterSyntax)
- {
- return parameterSyntax.AttributeLists.Any(attributeList =>
- attributeList.Attributes.Any(attribute =>
- GetCanonicalTypeName(compilation, attribute) == TypePointerSizedAttribute));
- }
- public void Initialize(GeneratorInitializationContext context)
- {
- context.RegisterForSyntaxNotifications(() => new SyscallSyntaxReceiver());
- }
- }
- }
|