Ассемблерные функции.

int33h-tm
int33h-tm

Во многих современных компиляторах С++ есть очень нетривиальные, но все же возможности написания ассемблерных вставок. Но их поддержка на уровне компилятора, в некоторых случаях, оставляет желать лучшего...Но давайте не будем заострять внимание именно на ассемблерных вставках, а подумаем немножечко глобальнее...
С++ наследник языка С, который являет собой язык процедурного программирования, следовательно объектом нашего рассмотрения должна стать функция(процедура)...

Идея довольно проста. Нам нужно придумать вариант реализации функции с внутренним ассемблерным кодом, которая сможет встроиться в наш код с помощью компилятора.(В современном gcc, есть возможность вызова некоторых команд процессора напрямую с помощью специальных intrinsic-функций, которые говорят компилятору выполнить прямую подстановку, но эти средства крайне ограниченны)
Недолгими размышлениями мы приходим к такому первоначальному ответу:

asm <retval> func([params]){
    //только aссемблерный код
}

(Ассемблерный код напрямую подставляется компилятором на место функции без всяких проверок. В случае вызова какой-либо функии, компилятор должен заменить ее вызов исходя из своей внутренней логики)
Но тут надо вспомнить, что языки С/С++ являются кроссплатформенными, чем ассемблер не может похвастаться. Это надо чем-то компенсировать... И тут вспоминается, что в последних стандартах(C++14) в язык была добавлена возможность помечать некоторые функции словом [[deprecated]]. Раз мы можем теперь помечать функции, то почему бы не пометить нашу функцию, как функцию для определенной платформы...

[[platform(x86)]]
asm <retval> func([params]){
    //ассемблерный код для платформы x86
}

Таких функций мы можем реализовать огромное множество для самых разных платформ(главное, чтобы совпадала сигнатура). Также желательно добавить еще способ дополнительного описания флагов процессора, таких как SSE, AVX и других, для того, чтобы использовать более расширенный набор команд.

Теперь встает вопрос, а как компилятору определить какую из них выбрать? Тут тоже можно дать ответ:
Подбирается наиболее подходящая функция в следующем порядке:

1)нужная платформа + флаги
Если есть несколько доступных(все указанные флаги у этого процессора есть) реализаций под конкретную платформу, но с разным кол-вом флагов, то будет выбираться вариант с наибольшим кол-вом.

2)общая реализация.(обычный С/С++ код)
Если нет общей реализации, то сообщить об ошибке компиляции под требуемую платформу.

Готово! Хотел бы я сказать, но ведь нужно еще подумать о передаче параметров...

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

//функция inc (префиксный оператор ++)
[[platform(x86), core_flags(...)]]
asm int inc(register int ebx) //register <type> <reg_name>
{
    inc ebx
    mov eax, ebx //по умолчанию возвращаемое значение кладется в регистр rax/eax/ax.
    ret
}

...

int main()
{
    int x = 0;
    ...
    inc(x);
//equal to:
//  mov ebx, &x
//  call inc
    ...
}

Вот теперь, наверно, все.

2
рейтинг
5 комментариев
yndx-antoshkka
Идея интересная но в таком виде работать не будет:
* вызывающей стороне нужно знать, какие регистры ассемблерная функция использует и не восстанавливает
* вызывающей стороне нужно знать memory order ограничения вашей ассемблерной функции
* copy elision на x86 использует rdi регистр, а не просто eax при возврате больших значений
* такая ассемблерная функция должна иметь internal linkage, иначе будут весёлые проблемы при её вызове сторонней библиотекой
* ...

Начните продумывать идею с https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
Там описаны дополнительные подсказки при ассемблерной вставке, необходимые компилятору для генерации эффективного кода вокруг ассемблерной вставки.
yndx-antoshkka
smertigdon
С текущей реализацией все ключевые слова ассемблера автоматически становятся ключевыми словами C++. Это сильно раздувает стандарт и грамматику, добавляет неоднозначности.
Тянуть один язык в другой - не шибко хорошая идея, хоть я и сам пользуюсь асм-вставками.
smertigdon
Fihtangolz

smertigdon, в чем проблема сделать inbrace лексику, просто берем написанно и с небольшими изменениями выплевываем ассеблеру тоже самое по сути что мс, каша в виде ""  в гцц откровенно говоря выглядит не слишком дружелюбно для чтения. Давно пора стандартиировать ситаксис вставок, конечно эту проблему можно решить внешней библиотекой с парой макросов, но ассемблерная вставка самом по себе громозкая нехватаеет туда еще пары макросов для консистентности 

Fihtangolz
Виктор Губин

Почему-бы не стандартизировать сам синтаксис ассемблерных вставок?

В GCC имеем:

__asm__ ( "addq %rbx,%rax\n" : "=a"(ret), "a"(lsh), "b"(rhs) : );

В MS VC++

__asm  
{
mov rax,lsh
mov rbx,rhs
add rax,rbx
mov ret,rax
}

Ассемблерный код понятное дело зависит от целевого процессора, но синтаксис самой вставки разный от компилятора к компилятору.

В итоге идетичный код нужно дублировать как - для типа CPU или OS, так и для разных компиляторов.

Виктор Губин
vlad-ger-m

Хорошая идея -

("вариант реализации функции с внутренним ассемблерным кодом, которая сможет встроиться в наш код с помощью компилятора. В современном gcc, есть возможность вызова некоторых команд процессора напрямую с помощью специальных intrinsic-функций". ) 

vlad-ger-m
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).
Все предложения