Re[4]: Приведение базового класса в конструкторе потомка
От: Hobbes Россия  
Дата: 03.09.19 18:35
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>А чертовщина — это то, что в MS VC++, например, обычный метод класса, возвращающий по значению POD-структуру размером до восьми байт, делает это через регистры. Но стоит сделать этот метод виртуальным, как он возвращает то же самое исключительно через временную переменную на стеке. Ладно бы возвращаемый элемент сам был классом, но это банальная POD-структура, которая от класса, в котором определяется метод, никак не зависит. Я в полных непонятках о причине такой особенности.


Возможно, причина в том, что в compile-time виртуальный метод не знает, в каком контексте его вызовут и чем там будут какие регистры заняты.
Re[5]: Приведение базового класса в конструкторе потомка
От: Mystic Artifact  
Дата: 03.09.19 18:38
Оценка:
Здравствуйте, Hobbes, Вы писали:

H>Возможно, причина в том, что в compile-time виртуальный метод не знает, в каком контексте его вызовут и чем там будут какие регистры заняты.


А не виртуальный знает что-ли?
Re[6]: Приведение базового класса в конструкторе потомка
От: Hobbes Россия  
Дата: 03.09.19 18:50
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

MA> А не виртуальный знает что-ли?


Ну... в общем случае тоже нет
Re[5]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 03.09.19 22:04
Оценка:
Здравствуйте, Hobbes, Вы писали:

H>Возможно, причина в том, что в compile-time виртуальный метод не знает, в каком контексте его вызовут и чем там будут какие регистры заняты.


Э-э-э... А как он тогда с другими параметрами и возвращаемыми значениями обращается?

Тут больше похоже на то, что когда-то просто облажались, перепутав где-то в таблицах признаки объекта класса и виртуальной функции, а когда это обнаружилось, метаться было уже поздно, и пришлось узаконить.
Re[6]: Приведение базового класса в конструкторе потомка
От: Mystic Artifact  
Дата: 03.09.19 22:17
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Э-э-э... А как он тогда с другими параметрами и возвращаемыми значениями обращается?

ЕМ>Тут больше похоже на то, что когда-то просто облажались, перепутав где-то в таблицах признаки объекта класса и виртуальной функции, а когда это обнаружилось, метаться было уже поздно, и пришлось узаконить.

Я тут на godbolt-е поигрался немного:

#include <utility>

typedef struct {
    int a;
    int b;
} ValueType;

class MyClass {
    public:
    ValueType method(int a, int b) {
        ValueType v;
        v.a = a;
        v.b = b + a;
        return v;
    };

    virtual ValueType virtual_method(int a, int b) {
        ValueType v;
        v.a = a;
        v.b = b + a;
        return v;
    };

    static ValueType static_method(int a, int b) {
        ValueType v;
        v.a = a;
        v.b = b + a;
        return v;
    };
};

int foo(int a, int b) {
  MyClass* x = new MyClass();
  auto v1 = x->method(a, b);
  auto v2 = x->virtual_method(b, a);
  auto v3 = MyClass::static_method(a, b);
  return v1.a + v2.b + v3.a * v3.b;
}


Как бы я не извращался — результат в rax. На clang тоже в rax.
Пример левой пяткой писан, потому, что он мне internal service error почему-то выдавал постоянно, пока его не "передернешь" с -O2 на -O0 и/или назад.
Примечательно, что при -O2 clang всё это выбрасывает нафиг, оставляя суть, а msvc не особо торопиться что-то выбрасывать.
Отредактировано 03.09.2019 22:17 Mystic Artifact . Предыдущая версия .
Re[4]: Приведение базового класса в конструкторе потомка
От: cserg  
Дата: 04.09.19 00:35
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>А чертовщина — это то, что в MS VC++, например, обычный метод класса, возвращающий по значению POD-структуру размером до восьми байт, делает это через регистры. Но стоит сделать этот метод виртуальным, как он возвращает то же самое исключительно через временную переменную на стеке.

А этот обычный метод класса встраиваемый или нет?
Re[7]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 04.09.19 11:28
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

MA> Как бы я не извращался — результат в rax. На clang тоже в rax.


А это уже странно. Они ж должны быть совместимы по ABI.
Re[5]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 04.09.19 11:29
Оценка:
Здравствуйте, cserg, Вы писали:

C>А этот обычный метод класса встраиваемый или нет?


Какая разница? Компилятор же для любого метода всегда генерит полноценную функцию, которую потом может свернуть при встройке. Именно о ней и речь.
Re[8]: Приведение базового класса в конструкторе потомка
От: Mystic Artifact  
Дата: 04.09.19 11:41
Оценка: 8 (1)
Здравствуйте, Евгений Музыченко, Вы писали:

MA>> Как бы я не извращался — результат в rax. На clang тоже в rax.

ЕМ>А это уже странно. Они ж должны быть совместимы по ABI.
Что-то вчера был не мой день видимо. Я и проект собирал, ошибся и пол дня он висел без дела, пока я не заметил.

В моём примере: msvc x64 v19.22 для method и virtual_method возвращается адрес на структуру, для static_method возвращается сама структура по значению.
А вот clang 8.0 — в обоих случаях возвращает структуру по значению (через регистр). С -O1 тела методов вообще идентичны.

Насчет должны быть совместимы по ABI — то, афаик, windows abi отличается от amd64 abi.

Вроде как похоже, что поведение вполне соответствует документации Calling Conventions: Return Values.

A scalar return value that can fit into 64 bits is returned through RAX; this includes __m64 types. Non-scalar types including floats, doubles, and vector types such as __m128, __m128i, __m128d are returned in XMM0. The state of unused bits in the value returned in RAX or XMM0 is undefined.

User-defined types can be returned by value from global functions and static member functions. To return a user-defined type by value in RAX, it must have a length of 1, 2, 4, 8, 16, 32, or 64 bits. It must also have no user-defined constructor, destructor, or copy assignment operator; no private or protected non-static data members; no non-static data members of reference type; no base classes; no virtual functions; and no data members that do not also meet these requirements. (This is essentially the definition of a C++03 POD type. Because the definition has changed in the C++11 standard, we don't recommend using std::is_pod for this test.) Otherwise, the caller assumes the responsibility of allocating memory and passing a pointer for the return value as the first argument. Subsequent arguments are then shifted one argument to the right. The same pointer must be returned by the callee in RAX.


Похоже на правду?
Отредактировано 04.09.2019 11:45 Mystic Artifact . Предыдущая версия .
Re[9]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 04.09.19 12:38
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

MA> Что-то вчера был не мой день видимо. Я и проект собирал, ошибся и пол дня он висел без дела, пока я не заметил.


И у меня ведь было стойкое убеждение, что я видел, как обычный (нестатический и невиртуальный) метод возвращал структуру по значению.

MA> В моём примере: msvc x64 v19.22 для method и virtual_method возвращается адрес на структуру, для static_method возвращается сама структура по значению.


Такая же хрень.

MA> Вроде как похоже, что поведение вполне соответствует документации Calling Conventions: Return Values.


И правда. Я с этим первые разы экспериментировал еще в 90-х, с тех пор и отложилось. Видать, ни разу не приглядывался с тех пор.

Вот только непонятно, на кой им понадобилось так разделять поведение статических и нестатических членов. Кроме случайной закрепившейся в практике ошибки, на ум ничего не приходит.
Re[10]: Приведение базового класса в конструкторе потомка
От: Mystic Artifact  
Дата: 04.09.19 12:45
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Вот только непонятно, на кой им понадобилось так разделять поведение статических и нестатических членов. Кроме случайной закрепившейся в практике ошибки, на ум ничего не приходит.

Я тоже не понимаю, и скорее всего они сами не понимают, тем более для других компиляторов это не является проблемой.

PS: Я помню, как-то решил обернуть некий то ли указатель, то ли что-то из int в структуру в C# и использовал в PInvoke (просто, что бы добавить какой-то типизированности). И всё работало, ведь на x86 такие структуры возвращаются по значению. Как же я был разочарован когда на линуксе это вообще не поднялось (ведь там они всегда возвращаются по указателю).
Re[6]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 12.09.19 08:29
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>объекты небольших структур с недорогим копированием, типа твоего Base, вполне допустимо передавать по значению.


Кстати, еще вспомнил, где это может быть полезно — при работе с множественными значениями типа HRESULT. Стандартно SUCCEEDED/FAILED — это макросы, можно сделать и через глобальные функции, но правильнее было бы оформить HRESULT в виде объекта со встроенными методами анализа, форматирования и т.п.

Но для таких вполне достаточно конструктора, оператора присваивания и методов обработки, а вот для других подобных структур было бы очень удобно иметь возможность и возвращать их по значению.
Re[7]: Приведение базового класса в конструкторе потомка
От: rg45 СССР  
Дата: 12.09.19 09:10
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:


R>>объекты небольших структур с недорогим копированием, типа твоего Base, вполне допустимо передавать по значению.


ЕМ>Кстати, еще вспомнил, где это может быть полезно — при работе с множественными значениями типа HRESULT. Стандартно SUCCEEDED/FAILED — это макросы, можно сделать и через глобальные функции, но правильнее было бы оформить HRESULT в виде объекта со встроенными методами анализа, форматирования и т.п.


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

ЕМ>Но для таких вполне достаточно конструктора, оператора присваивания и методов обработки, а вот для других подобных структур было бы очень удобно иметь возможность и возвращать их по значению.


Так а в чем проблема, возвращай, пожалуйста — все необходимые специальные функции-члены генерируются компилятором автоматически, главное ему не мешать. Ну либо предоставлять собственные версии, когда это действительно необходимо. А начиная с C++17 возвращать по значению можно вообще что угодно, независимо от доступности конструкторов — благодаря guaranteed copy/move elision. Это обсуждалось здесь не так давно: http://rsdn.org/forum/cpp/7421860.1
Автор: _NN_
Дата: 17.04.19
. Или ты снова подразумеваешь возврат через регистры vs возврат черерез стек?
--
http://static.skaip.su/img/emoticons/v2/ffffff/headbang.gif
Re[8]: Приведение базового класса в конструкторе потомка
От: Евгений Музыченко Франция http://software.muzychenko.net/rus
Дата: 17.09.19 09:44
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>мне очень трудно представить, чтоб передача параметра по ссылке, вместо передачи по значению, могла бы нанести ощутимый урон производительности.


Да понятно, что реального смысла в такой экономии нет — просто раздражает очевидная странность/тупость компилятора.

R>Так а в чем проблема, возвращай, пожалуйста — все необходимые специальные функции-члены генерируются компилятором автоматически, главное ему не мешать.


Я как посмотрю иногда, сколько кода автоматически генерит компилятор на все эти определения — плакать хочется.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.