-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support closed generic types. #53
Comments
Found fist problem at symbol mangling system. We have to make stable mangling symbols, I'll rewrite the type and method symbol name mangling algorithm. For example: For example (planning closed generic type, not tested): It's very redundant but stable and safe. And I'm planning will fix by append (readable, useful) alias names the final step. |
Changing apply full overload symbol naming. #53
NOTE: It's interesting about how to fit the array types into generic symbol system, I feel the array types understand making better:
Ofcourse |
MEMOIZED: Higher Kinded Polymorphism / Generics on Generics |
Today, redesigned method overriding calculation at Center CLR Try development meetup #8 (In japanese). I'll update CalculateVirtualMethods() and remove overload index related codes. |
Hello. I propose generic implementation idea. Implementation idea
|
Thanks for the sample code. Very interesting implementation. I see that you use runtime type information to achieve this. We can already get the size ( My idea at the moment (not clearly formed) is to use C macros for the expansion. The other problem is that readability will be poor, and I'm not sure I can go any further without resorting to a C++ template. (I have no plans to go literal "IL2C++", the C++ compiler is too slow :) If you think you can fill in the rest of your idea, you could try applying it directly to IL2C.Core. I am currently planning to work on Release 0.5, and the rest of the work will mainly be to improve the build environment and fix the documentation. Therefore, I do not plan to do much work on IL2C.Core for a while. (The Core unit test code will be significantly modified in relation to #100.) (This does not mean that I want to include your code in Release 0.5. I'm not in a hurry, so take it easy on me ;) |
Thank you for your reply.
it mens changing from "TypeInfo" to "IL2C_RUNTIME_TYPE", and "IL2C_RUNTIME_TYPE" can get from "il2c_get_header__". /* System_Object* */void* obj;
IL2C_RUNTIME_TYPE generic_T = il2c_get_header__(obj)->type; void Extensions_GenericPassThrough_T(IL2C_RUNTIME_TYPE generic_T, void *result, void *x) {
// .locals init (
// [0] !!T
// )
void *local_0;
void *stack_0;
void *stack_1;
uint32_t runtimeSize_T;
runtimeSize_T = il2c_sizeof__(generic_T)
local_0 = NULL;
stack_0 = alloca(runtimeSize_T);
// IL_0000: nop
// IL_0001: ldarg.0
memcpy(stack_0, genericArg_x, runtimeSize_T);
// IL_0002: stloc.0
memcpy(local_0, stack_0, runtimeSize_T);
// IL_0003: br.s IL_0005
// IL_0005: ldloc.0
memcpy(stack_1, local_0, runtimeSize_T);
// IL_0006: ret
memcpy(result, stack_1, runtimeSize_T);
}
void Extensions_GenericPassThroughTest() {
// .locals init (
// [0] int32 a
// )
System_Int32 a_System_Int32;
System_Int32 stack_0_0;
// IL_0000: nop
// IL_0001: ldc.i4.s 10
stack_0_0 = 10;
// IL_0003: call !!0 Extensions::GenericPassThrough<int32>(!!0)
Extensions_GenericPassThrough_T(il2c_typeof(System_Int32), &stack_0_0, &stack_0_0);
// IL_0008: stloc.0
a_System_Int32 = stack_0_0;
// IL_0009: ret
} I'm planning follows.
|
(Sorry it's a bit long. Since you seemed to be Japanese, I'll put the manuscript I wrote in Japanese on gist. You can reply there, but it would be helpful if you could also add the English translation here so that others can refer to it. deepl is also fine :) That code is fine for how to get runtime type information. (Perhaps you should define a macro in il2c.h.) I still don't understand all the sample code you wrote, but to get the member offsets of the structure, you can do the following.
public struct MultipleInsideValueTypeType
{
public string Value1;
public ObjRefInsideValueTypeType Value2;
public ObjRefInsideObjRefType Value3;
public MultipleInsideValueTypeType(string value1, string value2, string value3)
{
this.Value1 = value1;
this.Value2 = new ObjRefInsideValueTypeType(value2);
this.Value3 = new ObjRefInsideObjRefType(value3);
}
}
//////////////////////
// [7] Runtime helpers:
// [7-10-1] VTable (Not defined, same as System.ValueType)
// [7-8] Runtime type information
IL2C_RUNTIME_TYPE_BEGIN(IL2C_RuntimeSystems_MultipleInsideValueTypeType, "IL2C.RuntimeSystems.MultipleInsideValueTypeType", IL2C_TYPE_VALUE, sizeof(IL2C_RuntimeSystems_MultipleInsideValueTypeType), System_ValueType, 3, 0)
IL2C_RUNTIME_TYPE_MARK_TARGET_FOR_REFERENCE(IL2C_RuntimeSystems_MultipleInsideValueTypeType, Value1)
IL2C_RUNTIME_TYPE_MARK_TARGET_FOR_VALUE(IL2C_RuntimeSystems_MultipleInsideValueTypeType, IL2C_RuntimeSystems_ObjRefInsideValueTypeType, Value2)
IL2C_RUNTIME_TYPE_MARK_TARGET_FOR_REFERENCE(IL2C_RuntimeSystems_MultipleInsideValueTypeType, Value3)
IL2C_RUNTIME_TYPE_END(); This is a macro that defines runtime type information, with three lines of definitions #define IL2C_RUNTIME_TYPE_MARK_TARGET_FOR_VALUE(typeName, fieldTypeName, fieldName) \
(uintptr_t)il2c_typeof(fieldTypeName), \
offsetof(typeName, fieldName), which corresponds to typedef const struct IL2C_MARK_TARGET_DECL
{
const IL2C_RUNTIME_TYPE valueType;
const uintptr_t offset;
} IL2C_MARK_TARGET;
struct IL2C_RUNTIME_TYPE_DECL
{
const char* pTypeName;
const uintptr_t flags;
const uintptr_t bodySize; // uint32_t
const IL2C_RUNTIME_TYPE baseType;
const void* vptr0;
const uintptr_t markTarget; // mark target count / custom mark handler (only variable type)
const uintptr_t interfaceCount;
//IL2C_MARK_TARGET markTargets[markTarget];
//IL2C_IMPLEMENTED_INTERFACE interfaces[interfaceCount];
}; In other words, code like
Since this calculation was also a very internal information of IL2C, I did not specifically define a macro for this calculation, but if necessary, you may define a macro. Now, besides the performance issues with
// +----------------------+ <-- pHeader
// | IL2C_REF_HEADER |
// +----------------------+ <-- pReference -------
// | : | ^
// | (Instance body) | | bodySize
// | : | v
// +----------------------+ ------- I still don't understand the need for the copy, but I have a feeling that the way to handle this depends on why the copy is needed. |
As you see,As you see, I'm Japanese. Generic T GC implementationThank you for your description of GC mark method. I simply describe generic T GC implementation. So, as follows I assign IL2C/IL2C.Runtime/src/Core/il2c_gc.c Lines 256 to 270 in 4c3b409
objref: Case of Generic T is ObjrefIf type of T is objref, it doesn't copy class fields.
Case of assignSystem_Object* x;
System_Object* local_0;
local_0 = x; Case of memcpySystem_Object* x;
System_Object** arg_x;
System_Object** local_0;
arg_x = &x;
local_0 = alloca(sizeof(System_Object*));
memcpy(local_0, arg_x, sizeof(System_Object*)) OutputCodetypedef struct Extensions_GenericPassThroughTest_EXECUTION_FRAME_DECL
{
const IL2C_EXECUTION_FRAME* pNext__;
const uint16_t objRefCount__;
const uint16_t valueCount__;
//-------------------- objref
//-------------------- value type
const IL2C_RUNTIME_TYPE x_type__; // generic type
const void* x_value_ptr__;
const IL2C_RUNTIME_TYPE local_0_type__; // generic type
const void* local_0_value_ptr__;
const IL2C_RUNTIME_TYPE local_1_type__; // generic type
const void* local_1_value_ptr__;
const IL2C_RUNTIME_TYPE local_2_type__; // generic type
const void* local_2_value_ptr__;
} Extensions_GenericPassThroughTest_T_EXECUTION_FRAME__;
void Extensions_GenericPassThrough_T(IL2C_RUNTIME_TYPE generic_T, void *result, void *x) {
// .locals init (
// [0] !!T
// )
uint32_t runtimeSize_T;
runtimeSize_T = il2c_sizeof__(generic_T);
Extensions_GenericPassThroughTest_T_EXECUTION_FRAME__ frame = {
...
generic_T, // x
alloca(runtimeSize_T),
generic_T, // local_0
alloca(runtimeSize_T),
generic_T, // local_1
alloca(runtimeSize_T),
generic_T,// local_2
alloca(runtimeSize_T)
};
// IL_0001: ldarg.0
// T is value type: copy member filelds to new instance
// T is object reference type: copy pointer to new local variable with memcpy.
// so, this is not Object.MemberwiseClone https://docs.microsoft.com/ja-jp/dotnet/api/system.object.memberwiseclone?view=net-6.0
memcpy(frame.stack_0, x, runtimeSize_T);
...
}
void Extensions_GenericPassThroughTestObj() {
// .locals init (
// [0] object a
// )
// IL_0000: nop
System_Object* a_System_Object;
System_Object* stack_0_0;
// IL_0001: newobj instance void [System.Runtime]System.Object::.ctor()
stack_0_0 = il2c_get_uninitialized_object(System_Object);
System_Object__ctor(stack_0_0);
// IL_0006: call !!0 C::GenericPassThrough<object>(!!0)
// pass pointer of pointer
Extensions_GenericPassThrough_T(il2c_typeof(System_Object), &stack_0_0, &stack_0_0);
// IL_000b: stloc.0
a_System_Object = stack_0_0;
// IL_000c: ret
} |
Hold a field in the execution frame with a raw pointer to the instance
Maybe your initial concern about using Generic type argument constraintsWe haven't examined instance member access yet, but accesses like public static string foo<T>(T value) =>
value.ToString(); Or access with public static string bar<T>(T value)
when T : IDisposable =>
value.Dispose(); Assuming a managed compiler like C# has (correctly) computed the constraints, IL2C simply casts the pointer (reinterpret_cast to the VTABLE layout type of In the case of interfaces, we need to calculate adjustor offset, but if we can determine which interface the specified member ( |
I have come up with a conversion process for the following process and report it below.
Now that I have a rough idea of how to implement generics using memcpy in handwritten C code, I would like to think about the details while actually implementing it in IL2C (output in C89). First, I will try to implement it for value types that do not require gc in static methods. As for |
In the generic implementation, I need to add class C
{
static void F<T>(int result, int generic_T) {}
} Current conversion process void C_F_T(void* result, IL2C_RUNTIME_TYPE generic, int result, int generic_T) {} So, I will try to escape strings used for type and variable names with the following rules. Escaping rules
I plan to make the above fixes separately from the generic implementation, but may I implement them? |
Sorry for the late reply.
Yes indeed, in the case of value type, I have no problem without vptr (pReference) (this is in consideration of allowing direct pointer references on the native side during interop)
Noted :) In particular, we need to add a
So, I was holding off. With the method you suggest, I would have to modify the translator as well as the existing runtime implementation. Until we make this modification, we should try to increase the runtime implementation as little as possible.
I think it's good (I was thinking it might be better to make it dirtier for the current method).
Do you want to use UTF-8? There are readability issues, but realistically I don't think there's much use of CJK or anything like that, just enough to be a target if umlauts or something like that is used. Although not a symbol, you might want to keep in mind bug #124 that we recently picked up, if you haven't seen it yet. I mistakenly put in
I think it is good. Or maybe it would be better to have it macro-expanded (though I'm not a bit sure if it would contribute to readability). For example : // System.Collections.Generic.List<System.Int32>
// this won't work.
#define GENERIC_ARG(args) _d_##args%%_b_
System__Collections__Generic GENERIC_ARG(System__Int32)
// not so good...
#define GENERIC_TYPE(type, args) type##_d_##args%%_b_
GENERIC_TYPE(System__Collections__Generic, System__Int32) like ? |
Idea
The text was updated successfully, but these errors were encountered: