D / DLang : Запрещение генерации кода встроенных функций, закрытых для модуля.

У меня есть модуль D, который, я надеюсь, содержит общедоступную и приватную части. Я пытался использовать ключевые слова private и static перед определениями функций. У меня есть функция, которую я хочу сделать доступной/общедоступной извне, и в идеале я хотел бы, чтобы она была встроена в сайт вызова. Эта функция вызывает другие внутренние функции модуля, которые должны быть закрытыми, то есть не вызываемыми извне. Вызовы к ним успешно встраиваются в модуль, и большая часть мусора устраняется CTFE плюс распространение известных констант. Однако компилятор GDC также генерирует копии этих внутренних подпрограмм, даже если они встроены там, где это необходимо, и не должны вызываться извне. Я компилирую с -O3 -frelease. Что я должен делать - должен ли я ожидать этого, даже если я использую static и/или private?

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

Как я упоминал ранее, я пытался использовать как private, так и static для этих внутренних функций, но мне не удалось подавить генерацию кода. Я мог бы понять это, если бы отладчик нуждался в копиях этих подпрограмм для установки точек останова. Я должен подчеркнуть, что это, возможно, можно как-то решить во время компоновки, насколько мне известно. Я не пытался связать программу, я просто смотрю на сгенерированный код в Matt Godbolt D Compiler Explorer с помощью GDC. Все можно превратить в шаблоны со списком параметров шаблона нулевой длины (например, auto my_fn()(in arg_t x)), пробовал, не помогает, но и не вредит.

Еще пара вещей, которые можно попробовать: я мог бы попытаться создать статический класс с закрытыми частями, как способ реализации пакета в стиле Ады. (Должно быть строго в одном экземпляре.) Я никогда не занимался C++, только огромное количество ассемблера и C профессионально. Так что это будет кривая обучения.

Единственное, что я могу придумать, это использовать определения вложенных функций в стиле Pascal/Ada, переместить внутренние подпрограммы внутрь тела вызывающих их функций. Но в этом есть масса минусов.

Грубый пример

module junk;

auto my_public_fn() {  return my_private_fn();  }

private
static // 'static' and/or 'private', tried both
auto my_private_fn() { xxx ; return whatever; }

person Cecil Ward    schedule 27.02.2017    source источник
comment
Я не смог сравнить LDC с GDC, потому что в настоящее время подпрограммы содержат некоторый расширенный ассемблерный код, специфичный для GCC. Я заметил, что нежелательные тела функций — это не только те, которые содержат asm, кстати.   -  person Cecil Ward    schedule 27.02.2017
comment
вам определенно не нужно static. это не имеет значения в объявлениях верхнего уровня.   -  person Cauterite    schedule 28.02.2017
comment
Я не пробовал связывать программу — хотя я мало что знаю о GDC, возможно, во время компоновки происходит удаление мертвого кода/свертывание COMDAT, чтобы эти функции исчезли. Возможно, GDC не утруждает себя попытками DCE на этом уровне, потому что ожидается, что компоновщик все равно позаботится об этом. Хотя я просто предположил…   -  person Cauterite    schedule 28.02.2017


Ответы (1)


Я только что коротко обсудил это с Иэном, и реализовать это не так просто, как кажется.

Во-первых, static имеет много значений в D, но значение C для локальной функции единицы перевода не является одним из них ;-)

Поэтому пометка этих функций как private кажется интуитивно понятной. В конце концов, если вы не можете получить доступ к функции из-за пределов единицы перевода и никогда не пропускаете адрес функции, почему бы не удалить ее? В этом случае он может быть либо полностью не использован, либо встроен во все вызывающие объекты.

Вот загвоздка: мы не можем знать наверняка, не используется ли функция:

private void fooPrivate() {}

/*template*/ void fooPublic()()
{
    fooPrivate();
}

При компиляции файла GDC ничего не знает о шаблоне fooPublic (поскольку шаблоны могут быть полностью проанализированы только при создании экземпляра), поэтому fooPrivate кажется неиспользуемым. При последующем использовании fooPublic в другом файле GDC будет полагаться на то, что fooPrivate уже создано в исходном коде — в конце концов, это не шаблон, поэтому он не передается в новый модуль.

Могут быть обходные пути, но вся эта проблема кажется нетривиальной. Мы также могли бы ввести для этого собственный атрибут gcc.attribute. Это вызовет те же проблемы с шаблонами, но, поскольку это конкретная аннотация для одного варианта использования (в отличие от private), мы можем полагаться на то, что пользователь сделает все правильно.

person jpf    schedule 02.03.2017
comment
Может ли компоновщик разобраться с этим, например реализовать модули obj в виде библиотеки, содержащей отдельные части? (Несколько десятилетий назад мне приходилось делать что-то подобное вручную в огромном проекте.) Достаточно просто для мне сказать, что я не тот, кто выполняет эту работу. Может быть кошмар. Я понятия не имею, что делает GCC C/C++. - person Cecil Ward; 03.03.2017
comment
Я не знаю, насколько это стоило бы усилий. Я блеял, потому что писал функции, которые использовались исключительно CTFE или всегда были встроенными, но сгенерированное тело просто никогда не использовалось, оставаясь лежать там. - person Cecil Ward; 03.03.2017
comment
Это имело бы гораздо меньшее значение, если бы вы могли легко контролировать размещение тела функции, горячность или холодность (это есть в GCC?), чтобы пользователь поместил все неиспользуемые тела функций вместе в «холодный» конец объекта или, лучше, все такие подразделы в их собственные внешние разделы должны быть объединены вместе. - person Cecil Ward; 03.03.2017
comment
Would it be possible for a linker to sort this out: В некоторых случаях да. Например, если приватная функция является частью основного исполняемого файла, компоновщик (GCC -ffunction-sections и LD --gc-sections) или, возможно, даже стандартный LTO может удалить приватную функцию. Но в случае, если частная функция находится в библиотеке, ее необходимо сохранить, поскольку шаблон может быть создан в другой библиотеке/основном исполняемом файле. solely employed by CTFE это также реальная проблема для встроенных проектов, где размер двоичного файла имеет значение. Я думаю, что только для функций CTFE мы могли бы распознать if (__ctfe){} защищенных функций. - person jpf; 04.03.2017
comment
А то наверное убрать эти функции. Или, может быть, добавить специальный атрибут для этого. Встроенные системы OTOH не имеют общих библиотек и могут более легко использовать оптимизацию всей программы. Полностью встроенные функции более сложны, хотя, возможно, флаг force-inline мог бы исключить функцию из объектного файла... hotness vs coldness не реализован в GDC, но его не должно быть сложно добавить в gcc.attribute. Отправьте запрос на улучшение на bugzilla.gdcproject.org, если вам нужны эти атрибуты функции. - person jpf; 04.03.2017
comment
Что-то, что я сам сделал, это использовать функции только для ctfe, которые существуют только для вызова в утверждениях. Например, используя assert( production_fn(x) == simple_slow_alg_ctfe(x) ) в качестве проверки работоспособности, сравнивая два алгоритма, один из которых ctfe-only чрезвычайно прост/очевиден. Я предполагаю, что это плохой пример, потому что это действительно не должно быть проблемой, которую я мог бы решить самостоятельно, используя блоки debug для условной компиляции? - person Cecil Ward; 06.03.2017
comment
Я также использовал длинные функции ctfe только для инициализации таблиц только для чтения. - person Cecil Ward; 06.03.2017