Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VC++ SSE code generation - is this a compiler bug?

A very particular code sequence in VC++ generated the following instruction (for Win32):

unpcklpd    xmm0,xmmword ptr [ebp-40h]

2 questions arise:

(1) As far as I understand the intel manual, unpcklpd accepts as 2nd argument a 128-aligned memory address. If the address is relative to a stack frame alignment cannot be forced. Is this really a compiler bug?

(2) Exceptions are thrown from at the execution of this instruction only when run from the debugger, and even then not always. Even attaching to the process and executing this code does not throw. How can this be??

The particular exception thrown is access violation at 0xFFFFFFFF, but AFAIK that's just a code for misalignment.


[Edit:] Here's some source that demonstrates the bad code generation - but typically doesn't cause a crash. (that's mostly what I'm wondering about)

[Edit 2:] The code sample now reproduces the actual crash. This one also crashes outside the debugger - I suspect the difference occurs because the debugger launches the program at different typical base addresses.

    // mock.cpp
    #include <stdio.h>
    struct mockVect2d
    {
        double x, y;
        mockVect2d()    {}
        mockVect2d(double a, double b) : x(a), y(b) {}
        mockVect2d operator + (const mockVect2d& u) {
            return mockVect2d(x + u.x, y + u.y);
        }
    };

    struct MockPoly
    {
        MockPoly() {}
        mockVect2d*    m_Vrts;
        double  m_Area;
        int     m_Convex;
        bool    m_ParClear;

        void ClearPar()  { m_Area = -1.; m_Convex = 0; m_ParClear = true; }

        MockPoly(int len) { m_Vrts = new mockVect2d[len]; }

        mockVect2d& Vrt(int i) {
            if (!m_ParClear) ClearPar();
            return m_Vrts[i];
        }

        const mockVect2d& GetCenter() { return m_Vrts[0]; }
    };


    struct MockItem
    {
        MockItem() : Contour(1) {}
        MockPoly Contour;
    };

    struct Mock
    {
        Mock() {}
        MockItem m_item;
        virtual int GetCount()                  { return 2; }
        virtual mockVect2d GetCenter()  { return mockVect2d(1.0, 2.0); }
        virtual MockItem GetItem(int i) { return m_item; }
    };

    void testInner(int a)
    {
        int c = 8;
        printf("%d", c);
        Mock* pMock = new Mock;
        int Flag = true;
        int nlr = pMock->GetCount();

        if (nlr == 0)
            return;

        int flr = 1;
        if (flr == nlr)
            return;

        if (Flag)
        {
            if (flr < nlr && flr>0) {
                int c = 8;
                printf("%d", c);

                MockPoly pol(2);
                mockVect2d ctr = pMock->GetItem(0).Contour.GetCenter();

                // The mess happens here:
                //          ; 74   :            pol.Vrt(1) = ctr + mockVect2d(0., 1.0);
                // 
                //          call ? Vrt@MockPoly@@QAEAAUmockVect2d@@H@Z; MockPoly::Vrt
                //              movdqa  xmm0, XMMWORD PTR $T4[ebp]
                //              unpcklpd xmm0, QWORD PTR tv190[ebp]      **** crash!
                //              movdqu  XMMWORD PTR[eax], xmm0

                pol.Vrt(0) = ctr + mockVect2d(1.0, 0.);
                pol.Vrt(1) = ctr + mockVect2d(0., 1.0);
            }
        }
    }

    void main()
    {
        testInner(2);
        return;
    }

If you prefer, download a ready vcxproj with all the switches set from here. This includes the complete ASM too.

like image 997
Ofek Shilon Avatar asked Oct 20 '22 16:10

Ofek Shilon


1 Answers

Update: this is now a confirmed VC++ compiler bug, hopefully to be resolved in VS2015 RTM.


Edit: The connect report, like many others, is now garbage. However the compiler bug seems to be resolved in VS2017 - not in 2015 update 3.

like image 62
Ofek Shilon Avatar answered Oct 27 '22 09:10

Ofek Shilon