Переопределить продвижение аргумента varargs в GCC - PullRequest
4 голосов
/ 01 октября 2019

Я работаю над куском кода для процессора ARM, работающего на архитектуре Cortex-M4 (с плавающей запятой одинарной точности, но не с двойной). Проблема, с которой я сталкиваюсь, заключается в том, что при использовании varargs компилятор пытается превратить мои плавающие числа в двойные. Есть ли способ отключить это? Или указать другую стратегию продвижения? Я просмотрел руководства GCC, но ничего не смог найти. Простой пример - попытка написать свой собственный printf ...

void myprintf(const char *fmt, ...) {
  // .. parse fmt and come across %f

  double dv = va_arg(args, double); // Compiles but doesn't link

  float fv = va_arg(args, float); // Doesn't compile (warns about promotion)
}

ОБНОВЛЕНИЕ: В соответствии с запросом, команда вызова, которую я ищу, - это myprintf("%f", 1.0f);. Проблема в том, что Cortex-M4 не поддерживает двойные значения, поэтому, когда 1.0f повышается до двойного ... ну, это не может. Поэтому чтение его с использованием va_arg(args, double) скомпилирует, но не скомпонует, так как различные функции __aeabi_, связанные с double, не существуют. Что я хочу сделать, так это отключить продвижение GCC в числах с плавающей запятой до двойных (если это возможно).

ОБНОВЛЕНИЕ: У меня есть текущий обходной путь, который в основном просто принимает указатели на поплавки вместо поплавков. Я все еще хотел бы отключить продвижение по службе, поскольку это не очень хорошее решение.

1 Ответ

4 голосов
/ 01 октября 2019

Преобразование аргументов vararg float в double не является причудой или дополнением gcc. Это требуется стандартом C. §6.5.2.3 (вызовы функций):

Если выражение, обозначающее вызываемую функцию, имеет тип, который не включает в себя прототип, целочисленные преобразования выполняются для каждого аргумента, а аргументы, имеющие тип float, переводятся в double. Они называются поощрениями аргументов по умолчанию . … Если выражение, которое обозначает вызываемую функцию, имеет тип, который включает в себя прототип, аргументы неявно преобразуются, как если бы путем присваивания, в типы соответствующих параметров, принимая тип каждого параметра вбыть безоговорочной версией объявленного типа. Многоточие в объявлении прототипа функции приводит к тому, что преобразование типа аргумента останавливается после последнего объявленного параметра. Повышение аргументов по умолчанию выполняется на конечных аргументах.

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

Обратите внимание, что в рамках ограничений стандарта невозможно просто сделать double синонимом для float, если float является 32-битным значениемМинимальные требования к точности в §5.2.4.2.2 фактически требуют, чтобы у двойника была как минимум 32-битная мантисса в дополнение к показателю степени и знаку. Это намного меньше точности, чем обеспечивается 64-битными двойными кодами IEEE-749, но, очевидно, это больше, чем может быть обеспечено в 32-битном значении.

Компилятор, безусловно, мог бы предоставить опцию, в которой double s представлены в менее точном и вычислительно более быстром формате, например, два float s, где мантиссы не перекрываются. (То есть показатели отличаются, по крайней мере, на ширину мантиссы.) Это представление или что-то подобное использовалось, по крайней мере, в одной унаследованной реализации IIRC, именно потому, что оно позволяет использовать оборудование с одинарной точностью для вычислений с двойной точностью,Это также делает приведение float к double тривиальным;нужно только сделать младшее значение 0.0. В качестве альтернативы, компилятор может использовать 32-битную мантиссу в сочетании с показателем short, что позволяет использовать целочисленное значение ALU.

Я не верю, что GCC также предлагает такую ​​альтернативу.

...