Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

[CppCodeGen] Simple test with enum fails on Linux x64 #2884

Closed
sergign60 opened this issue Mar 3, 2017 · 8 comments
Closed

[CppCodeGen] Simple test with enum fails on Linux x64 #2884

sergign60 opened this issue Mar 3, 2017 · 8 comments

Comments

@sergign60
Copy link
Contributor

The following test fails on x64 Linux with the latest version of corert

using System;

namespace Hello
{
    internal class Program
    {
        enum orientation : byte
        {
            north = 1,
            south = 2,
            east = 3,
            west = 4
        }
        private static int Main()
        {
            orientation myDir = orientation.north;
            return Convert.ToInt32(myDir);
        }
    }
}

the listing of the execution in gdb

(gdb) run
Starting program: /home/signatov/corert/tests/src/Simple/Hello/bin/Debug/x64/native/Hello 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffdc895700 (LWP 27871)]

Thread 1 "Hello" received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x0000000000754289 in System_Private_CoreLib::System::Convert::ToInt32 (_a0=0x7fffdc898b48) at /home/signatov/corert/tests/src/Simple/Hello/obj/Debug/x64/native/Hello.cpp:297709
#2  0x0000000000752285 in Hello::Hello::Program::Main () at /home/signatov/corert/tests/src/Simple/Hello/obj/Debug/x64/native/Hello.cpp:276128
#3  0x00000000007359ed in Hello::_Module_::MainMethodWrapper () at /home/signatov/corert/tests/src/Simple/Hello/obj/Debug/x64/native/Hello.cpp:274913
#4  0x0000000000735736 in Hello::_Module_::StartupCodeMain (_a0=1, _a1=140737488346376) at /home/signatov/corert/tests/src/Simple/Hello/obj/Debug/x64/native/Hello.cpp:262561
#5  0x0000000000735a7b in __managed__Main (_a0=1, _a1=140737488346376) at /home/signatov/corert/tests/src/Simple/Hello/obj/Debug/x64/native/Hello.cpp:262569
#6  0x0000000000b2993a in main (argc=1, argv=0x7fffffffdd08) at /home/signatov/corert/src/Native/Bootstrap/main.cpp:319
(gdb) b System_Private_CoreLib::System::Convert::ToInt32
Breakpoint 1 at 0x7541fc: file /home/signatov/corert/tests/src/Simple/Hello/obj/Debug/x64/native/Hello.cpp, line 297692.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/signatov/corert/tests/src/Simple/Hello/bin/Debug/x64/native/Hello 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffdc895700 (LWP 27875)]

Thread 1 "Hello" hit Breakpoint 1, System_Private_CoreLib::System::Convert::ToInt32 (_a0=0x7fffdc898b48)
    at /home/signatov/corert/tests/src/Simple/Hello/obj/Debug/x64/native/Hello.cpp:297692
297692		int32_t _l0 = 0;
(gdb) n
297697		void* _1 = _a0;
(gdb) n
297698		if (_1 == 0) {
(gdb) n
297701	}
(gdb) n
297705		void* _2 = _a0;
(gdb) n
297706		void* _3 = __castclass(_2, ::System_Private_CoreLib::System::IConvertible::__getMethodTable());
(gdb) n
297708	void*_4 = (void*) ((ToInt32) System_Private_CoreLib::System::Runtime::DispatchResolve::FindInterfaceMethodImplementationTarget(::System_Private_CoreLib::System::Object::get_EEType((::System_Private_CoreLib::System::Object*)_3), ((::System_Private_CoreLib::Internal::Runtime::EEType *)(::System_Private_CoreLib::System::IConvertible::__getMethodTable())), (uint16_t)(::System_Private_CoreLib::System::IConvertible::__getslot__ToInt32(_3))));
(gdb) n
297709		int32_t _5 = ((ToInt32)_4)((::System_Private_CoreLib::System::IConvertible*)_3, (::System_Private_CoreLib::System::IFormatProvider*)0);
(gdb) p _4
$1 = (void *) 0x0
(gdb) p _3
$2 = (void *) 0x7fffdc898b48
(gdb) s

Thread 1 "Hello" received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()

I commented #line generation in the file src/ILCompiler.Compiler/src/CppCodeGen/ILToCppImporter.cs and added "-g" in /src/BuildIntegration/Microsoft.NETCore.Native.Unix.props

    <CppCompilerAndLinkerArg Condition="'$(Configuration)' == 'Debug'" Include="-O0 -g" />
@sergign60
Copy link
Contributor Author

CC: @Dmitri-Botcharnikov @jkotas

@sergign60 sergign60 changed the title [x64 Linux] Simple test with enum is broken on Linux x64 [x64 Linux] Simple test with enum fails on Linux x64 Mar 3, 2017
@jkotas jkotas changed the title [x64 Linux] Simple test with enum fails on Linux x64 [CppCodeGen] Simple test with enum fails on Linux x64 Mar 3, 2017
@sergign60 sergign60 changed the title [CppCodeGen] Simple test with enum fails on Linux x64 [CppCodeGen] Simple test fails with enum fails on Linux x64 Mar 3, 2017
@sergign60 sergign60 changed the title [CppCodeGen] Simple test fails with enum fails on Linux x64 [CppCodeGen] Simple test with enum fails on Linux x64 Mar 3, 2017
@jkotas
Copy link
Member

jkotas commented Mar 3, 2017

This is likely caused by missing generator of unboxing stub in CppCodeGen. It is not Linux specific - same crash will happen on Windows too.

The CppCodeGen is not as far as the RyuJIT CodeeGen (list of issues CodeGen-Cpp ). This test works fine with RyuJIT, so the alternative is to start bringing up the RyuJIT backend.

@benpye
Copy link
Contributor

benpye commented May 21, 2017

@jkotas Just had a look at this issue, though struggled to translate the approach RyuJIT took to the CPP generator. Is there any documentation that would allow me to better understand the work needed here (corert seems less will documented than coreclr unfortunately). Do you think another of the open CPP issues would be better to try and get an understanding of the code?

@jkotas
Copy link
Member

jkotas commented May 22, 2017

@benpye Welcome back Ben!

Even simpler repro - interface method calls on boxed value types do not work:

using System;

internal class Program
{
    interface IMyInterface
    {
        void MyMethod();
    }
    struct MyValueType : IMyInterface
    {
        void IMyInterface.MyMethod()
        {
            Console.WriteLine("IMyInterface.MyMethod");
        }
    }
    private static void Main()
    {
        object o = new MyValueType();
        ((IMyInterface)o).MyMethod();
    }
}

Here is how to start debugging it:

void*_7 = (void*) ((MyMethod) System_Private_CoreLib::System::Runtime::DispatchResolve::FindInterfaceMethodImplementationTarget(::System_Private_CoreLib::System::Object::get_EEType((::System_Private_CoreLib::System::Object*)_6), ((::System_Private_CoreLib::Internal::Runtime::EEType *)(::repro::Program_IMyInterface::__getMethodTable())), (uint16_t)(::repro::Program_IMyInterface::__getslot__MyMethod(_6))));
((MyMethod)_7)((::repro::Program_IMyInterface*)_6); <- this line causes the AV because of _7 is null

FindInterfaceMethodImplementationTarget finds the pointer to the method to call, and the next line calls it. The crash means that FindInterfaceMethodImplementationTarget have not found the interface method implementation.

The interface method implementation is pretty similar between CoreCLR and CoreRT.: https://github.com/dotnet/coreclr/blob/master/Documentation/botr/virtual-stub-dispatch.md The low level assembly tricks and encoding of the dispatch maps are different, but the overall scheme is the same.

The crash is likely caused by bad dispatch map emitted by the compiler. InterfaceDispatchMapNode is what holds the dispatch map in the compiler.

Do you think another of the open CPP issues would be better to try and get an understanding of the code?

I think this issue is a good one to start with.

@benpye
Copy link
Contributor

benpye commented May 24, 2017

#3670 fixed the example given by @jkotas above, however the original and a simplified version are still broken.

using System;

namespace EnumTest
{
    internal class Program
    {
        enum Test { A }

        private static void Main()
        {
            Console.WriteLine(Convert.ToInt32(Test.A));
        }
    }
}

As in the PR, System_Private_CoreLib::System::Runtime::DispatchResolve::FindInterfaceMethodImplementationTarget is returning NULL , it seems that FindImplSlotForCurrentType (at https://github.com/dotnet/corert/blob/master/src/Runtime.Base/src/System/Runtime/DispatchResolve.cs#L51 ) fails until pCur is NULL, and so NULL is returned.

EDIT: So far as I can tell this is occurring exclusively for enums. Does RyuJIT take any additional steps for enums? The method table for enum types is full of the non-unboxing methods, whilst other value types get given (correctly) the unboxing stubs.

@jkotas
Copy link
Member

jkotas commented May 24, 2017

Does RyuJIT take any additional steps for enums?

I do not think so. Looks like a bug in the CppCodeGen - using underlying enum type instead of actual enum type, or something similar.

@benpye
Copy link
Contributor

benpye commented May 24, 2017

Hm, alright. At a quick glance it looks like CppCodeGen doesn't do much differently for enums compared to other value types, aside from a little different name mangling. Will have another look over the weekend.

@cshung
Copy link
Member

cshung commented Jul 16, 2019

It appears to me that with the latest code on the master branch, the bug does not repro anymore (with all the code snippets above). Closing this issue for now. Feel free to re-open if this is not the case for you @sergign60 @jkotas @benpye

@cshung cshung closed this as completed Jul 16, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants