Автор Тема: Трансляция Oberon в C#  (Прочитано 9599 раз)

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Трансляция Oberon в C#
« Ответ #15 : Апрель 19, 2012, 08:09:51 am »
Дочитал в книге Вирта и Гуткнехта "Проект Оберон" до места где описывается передача записей в процедуру как VAR параметров (стр. 353-354). Оказывается тэг типа надо размещать не в самой записи. Если запись размещается в динамической памяти, то тэг типа записывается там непосредственно перед ней. А если запись размещена статически, то компилятор тег её типа и так знает и нет нужды его куда-то ещё писать. При передаче записи в процедуру VAR параметром компилятор фактически передаёт в процелуру не одну, а две величины: тэг типа и адрес записи.

То есть при трансляции Oberon в C# надо будет сделать как-то так:

PROCEDURE Foo (VAR r: Record);  ---->   void Foo (TypeTag* t, Record* r); здесь t - тэг типа записи r.

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Трансляция Oberon в C#
« Ответ #16 : Сентябрь 03, 2012, 11:49:41 am »
По производительности реализация на C# будет медленнее из-за того что в  C# вместо указателей на процедуры используются делегаты (которые суть динамические объекты). Тормозить будет конвертация из указателя в делегат.

Лучше будет, видимо, вовсе не использовать GetDelegateForFunctionPointer/GetFunctionPointerForDelegate, а класть делегаты в глобально доступный статический массив и вместо указателя на функцию использовать индекс в этом массиве делегатов. Проигрыш по производительности всё равно будет, но не таким большим как при постоянном мусорении объектами делегатов.

Голые указатели на процедуры в C# отсутствуют, однако они есть в CIL.

Берём С++ и пишем следующий код:
typedef void (__stdcall *Procedure)(int, int);

void __stdcall MyProcedure (int n, int m)
{
    System::Console::WriteLine("MyProcedure n={0} m={1}", n, m);
}

int main(array<System::String ^> ^args)
{
    Procedure p = &MyProcedure;
    p(1, 2);
    p(3, 4);
    p(5, 6);
    System::Console::ReadLine();
    return 0;
}
Компилируем в консольное дотнетное приложение, затем смотрим получившийся IL-код посредством утилиты IL DASM. Вот что получается:
.method assembly static int32  main(string[] args) cil managed
{
  // Code size       42 (0x2a)
  .maxstack  3
  .locals ([0] method unmanaged stdcall void modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall) *(int32,
           int32) p,
           [1] int32 V_1)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.1
  IL_0002:  ldsfld     int32** __unep@?MyProcedure@@$$FYGXHH@Z
  IL_0007:  stloc.0
  IL_0008:  ldc.i4.1
  IL_0009:  ldc.i4.2
  IL_000a:  ldloc.0
  IL_000b:  calli      unmanaged stdcall void modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall)(int32,int32)
  IL_0010:  ldc.i4.3
  IL_0011:  ldc.i4.4
  IL_0012:  ldloc.0
  IL_0013:  calli      unmanaged stdcall void modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall)(int32,int32)
  IL_0018:  ldc.i4.5
  IL_0019:  ldc.i4.6
  IL_001a:  ldloc.0
  IL_001b:  calli      unmanaged stdcall void modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall)(int32,int32)
  IL_0020:  call       string [mscorlib]System.Console::ReadLine()
  IL_0025:  pop
  IL_0026:  ldc.i4.0
  IL_0027:  stloc.1
  IL_0028:  ldloc.1
  IL_0029:  ret
} // end of method 'Global Functions'::main
Короче, не смотря на то что эффективная трансляция Оберона в C# невозможна, она возможна непосредственно в CIL. Я раньше почему-то думал, что C# и CIL соотносятся друг с другом примерно 1:1. Ан нет, CIL сильно помощнее.

valexey

  • Administrator
  • Hero Member
  • *****
  • Сообщений: 1990
    • Просмотр профиля
Re: Трансляция Oberon в C#
« Ответ #17 : Сентябрь 03, 2012, 04:11:37 pm »
Короче, не смотря на то что эффективная трансляция Оберона в C# невозможна, она возможна непосредственно в CIL. Я раньше почему-то думал, что C# и CIL соотносятся друг с другом примерно 1:1. Ан нет, CIL сильно помощнее.
Чем ближе к машине (хоть реальной хоть виртуальной) язык, тем он мощнее (в плане вычислительных возможностей, а не построения абстракций). То же самое можно сказать и про Си vs asm (без разницы какой именно asm, какая архитектура).

Кстати, а в .net есть API для генерации байткода, или проще сгенерировать CIL-текст а затем прогнать мелкософтовским ассемблером чтобы это дело собрал в байткод?
"но сейчас, чтобы компенсировать растущую мощность компьютеров, программисты используют фреймворки"

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Трансляция Oberon в C#
« Ответ #18 : Сентябрь 03, 2012, 04:56:41 pm »
Кстати, а в .net есть API для генерации байткода, или проще сгенерировать CIL-текст а затем прогнать мелкософтовским ассемблером чтобы это дело собрал в байткод?
Есть. Написал примерчик: class Program
{
public delegate void Procedure (int n);
public delegate void Executer (System.IntPtr pointer, int n);

public static readonly Executer executer;
public static readonly Procedure procedure;

static Program ()
{
System.Type Void = typeof(void);
System.Type Pointer = typeof(System.IntPtr);
System.Type Integer = typeof(System.Int32);

System.Reflection.Emit.DynamicMethod d = new System.Reflection.Emit.DynamicMethod("Executer",
Void, new System.Type[] { Pointer, Integer });

System.Reflection.Emit.ILGenerator g = d.GetILGenerator();

g.Emit(System.Reflection.Emit.OpCodes.Ldarg_1);
g.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
g.EmitCalli(System.Reflection.Emit.OpCodes.Calli,
System.Runtime.InteropServices.CallingConvention.StdCall, Void, new System.Type[] { Integer });
g.Emit(System.Reflection.Emit.OpCodes.Ret);

executer = (Executer)d.CreateDelegate(typeof(Executer));
procedure = Test;
}

public static void Test (int n)
{
System.Console.WriteLine("Test n={0}", n);
}

static void Main (string[] args)
{
System.IntPtr pointer = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(procedure);
try
{
executer(pointer, 42);
}
catch (System.Exception e)
{
System.Console.WriteLine("Exception: {0}", e);
}
System.Console.ReadLine();
}
}

В этом примере я генерирую код процедуры Executer, которая будучи написанной на Си выглядела бы как-то так:

typedef void (__stdcall *Procedure)(int);

void Executer (void* pointer, int n)
{
     ((Procedure)pointer)(n);
}

То есть она принимает указатель void* приводит его к типу Procedure и вызывает передавая один аргумент.

Я вроде всё правильно написал, осталось только заставить программу заработать. Она, зараза, не хочет, кидает эксепшн:

Exception: System.Security.VerificationException: Operation could destabilize the runtime.
   at Executer(IntPtr , Int32 )
   at Program.Main(String[] args) in D:\PROJECTS\Test\NativeFunctionPointerCallConsoleApplication\Program.cs:line 39

Справка говорит, что:

Исключение VerificationException генерируется, если в политику безопасности входит требование строгой типизации, а в ходе проверки невозможно определить, выполнено ли оно в коде.


Сижу думаю как же теперь отключить верификацию, которая по дефолту включена...  >:( >:( >:(

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Трансляция Oberon в C#
« Ответ #19 : Сентябрь 05, 2012, 12:51:04 pm »
Сижу думаю как же теперь отключить верификацию, которая по дефолту включена...  >:( >:( >:(
Победил беду.

Как отключить верификацию я не понял, но неожиданно обнаружил, что если вызвать другой конструктор динамического метода, в который надо передавать ещё и модуль, и в качестве модуля передать "ядрёный" модуль = typeof(void).Module, то всё работает. Видимо у "ядрёного" модуля верификация отключена.

Итого, работающий пример генерации IL кода на лету:
unsafe class Program
{
public delegate void Procedure (int n);
public delegate void Executer (void* pointer, int n);

public static readonly Executer executer;
private static readonly Procedure procedure;

static Program ()
{
System.Type Void = typeof(void);
System.Type Pointer = typeof(void*);
System.Type Integer = typeof(System.Int32);

System.Reflection.Emit.DynamicMethod dm = new System.Reflection.Emit.DynamicMethod("Executer",
Void, new System.Type[] { Pointer, Integer }, typeof(void).Module);

System.Reflection.Emit.ILGenerator g = dm.GetILGenerator();
g.Emit(System.Reflection.Emit.OpCodes.Ldarg_1);
g.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
g.EmitCalli(System.Reflection.Emit.OpCodes.Calli,
System.Runtime.InteropServices.CallingConvention.StdCall, Void, new System.Type[] { Integer });
g.Emit(System.Reflection.Emit.OpCodes.Ret);

executer = (Executer)dm.CreateDelegate(typeof(Executer));
procedure = Test;
}

public static void Test (int n)
{
System.Console.WriteLine("Test n={0}", n);
}

static void Main (string[] args)
{
void* pointer = (void*)System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(procedure);
executer(pointer, 42);
System.Console.ReadLine();
}
}