Есть ли способ использовать выражения, оцениваемые во время компиляции со встроенным asm в gcc? - PullRequest
0 голосов
/ 23 сентября 2010

У меня есть некоторый код, который в основном должен использовать небольшое выражение в операторе сборки, где выражение довольно тривиально, как i * 4, но GCC, похоже, не осознает этого во время компиляции (пробовал без флага -O,и -O3).Ни ограничения "i", ни "n" не работают в следующем фрагменте для третьего использования.

#include <stdint.h>
#include <stdlib.h>

#define SHIFT(h, l, c) __asm__ volatile ( \
    "shld %2, %1, %0\n\t" \
    "sal %2, %1\n\t" \
    : "+r"(h), "+r"(l) : "i"(c))

void main(void) {
  uint64_t a, b;
  SHIFT(a, b, 1); /* 1 */
  SHIFT(a, b, 2*4); /* 2 */
  size_t i;
  for(i=0; i<24; i++) {
    SHIFT(a, b, (i*4)); /* 3 */
  }
}

Предоставление этой ошибки:

temp.c:15: warning: asm operand 2 probably doesn’t match constraints
temp.c:15: error: impossible constraint in ‘asm’

Я также пытался

"shld $" #c ", %1...

, но это имеет свою собственную проблему, потому что паренсы остаются при строковом преобразовании.Я собираюсь развернуть весь цикл, но, похоже, что -funroll-all-loop не происходит достаточно рано, чтобы заставить i * 4 стать константой.Есть идеи?Альтернатива довольно уродливая, но если бы был способ автоматизировать это в макросе, это было бы лучше, чем ничего:

SHIFT(a, b, 1);
SHIFT(a, b, 2);
...
SHIFT(a, b, 24);

Ответы [ 5 ]

0 голосов
/ 11 ноября 2014

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

Есть несколько проблем с кодом OP, но с небольшой очисткой вы получите:

#include <stdint.h>

#define SHIFT(h, l, c) __asm__ volatile ( \
    "shld %b2, %1, %0\n\t" \
    "sal %b2, %1\n\t" \
    : "+r"(h), "+r"(l) : "Jc"(c))

int main(void) {
  uint64_t a, b;
  a = b = 0;
  SHIFT(a, b, 1); /* 1 */
  SHIFT(a, b, 2*4); /* 2 */
  size_t i;
  for(i=0; i<16; i++) {
    SHIFT(a, b, (i*4)); /* 3 */
  }
}

Наиболее значимые изменения:

  • Использование «Jc» для ограничения для (c). Это позволяет gcc использовать немедленное, если это возможно, но при необходимости возвращается к rcx (т.е. значение не помещается в «J» или значение не известно во время компиляции).
  • Использование% b2 вместо просто% 2. Это дает нам cl вместо rcx, чего требуют эти инструкции.
  • Изменение размера цикла до 16. sal и shld допускают сдвиги только 0-63 на x64 (0-31 на x86).

Скомпилировано с -O2 -m64 -funroll-all-loops -S, мы видим:

/APP
 # 12 "shl.cpp" 1
        shld $1, %rdx, %rax
        sal $1, %rdx

 # 0 "" 2
 # 13 "shl.cpp" 1
        shld $8, %rdx, %rax
        sal $8, %rdx

 # 0 "" 2
 # 16 "shl.cpp" 1
        shld $0, %rdx, %rax
        sal $0, %rdx

 # 0 "" 2
 # 16 "shl.cpp" 1
        shld $4, %rdx, %rax
        sal $4, %rdx
...

 # 0 "" 2
 # 16 "shl.cpp" 1
        shld $60, %rdx, %rax
        sal $60, %rdx

 # 0 "" 2
/NO_APP

Что интересно, если вы используете i * 6 вместо i * 4, вы видите, что gcc использует немедленные значения до 60, а затем начинает использовать cl.

Тад!

0 голосов
/ 24 апреля 2013

Ваш "уродливый" путь может быть достигнут с помощью библиотеки препроцессора Boost (на самом деле это набор макросов cpp и единственная часть Boost, которую можно использовать с простым C):

#include <boost/preprocessor/repetition/repeat.hpp>

#define SHIFT_a(z, CNT, b) __asm__ volatile ( \
    "shld %2, %1, %0\n\t" \
    "sal %2, %1\n\t" \
    : "+r"(a), "+r"(b)
    : "i"(CNT * 4)
    : "cc");

void main(void) {
    uint64_t a, b;

// whatever ...

    BOOST_PP_REPEAT_FROM_TO(1, 25, SHIFT_a, b)
}

Бит "некрасивый", который остается в этом, заключается в том, что макросы BOOST_PP_REPEAT*, которые могут "перебирать", ограничены одним предоставленным пользователем аргументом, поэтому вы должны "встраивать" либо a, либо b в этом примере в фактическое имя макроса. Может быть, это можно обойти другим косвенным уровнем (чтобы преобразовать SHIFT(a) в SHIFT_a?). Не пробовал.

0 голосов
/ 25 сентября 2010

Не знаю, почему вы смещаетесь влево на 23 * 4 = 92, но ...

Там может быть. Вы можете использовать __builtin_constant_p () и __builtin_choose_expr (), чтобы выбрать выражение для компиляции; что-то вроде

__builtin_choose_expr(__builtin_constant_p(c), SHIFT(h, l, c), slower_code_here);

Если он выбирает slower_code_here, то он "не может" определить, что c было константой. Если он жалуется на «невозможное ограничение», то он знает, что оно постоянное, но по какой-то причине ему не удается превратить его в немедленное.

Иногда удивительно, что он думает и не является постоянным; Я играл на днях, и он жаловался на что-то вроде __builtin_choose_expr(sizeof(char[__builtin_choose_expr(..., 1, -1)]),...).

(Я предполагаю, что порядок% 2,% 1,% 0 преднамеренный; я ожидал бы% 0,% 1,% 2, но документация расплывчатая, и я никогда не могу вспомнить, какой синтаксис asm используется .)

0 голосов
/ 31 января 2013

Вы предполагаете, что компилятор развернет ваш цикл и подставит значение i * 4 каждый раз ... это немного много, чтобы предположить. * 4 выглядит так, как будто вы хотите какую-то модификацию адресации, почему бы не передать i и написать инструкцию для выполнения * 4? внимательно посмотрите на ограничения GCC и убедитесь, что ваши инструкции действительно используют все комбинации, которые ваши ограничения могут создать.

0 голосов
/ 23 сентября 2010

Есть ли какая-либо конкретная причина пометить блок asm как volatile ? Практически невозможно провести какую-либо оптимизацию, пока присутствует volatile .

...