Необязательно, только в условном блоке компиляции. Компилятор полностью удаляет оператор из сборки выпуска? - PullRequest
2 голосов
/ 18 октября 2019

Например:

struct L {
    #if DEBUG
    static let og:((String) -> Void)? = { print($0) }
    #else
    static let og:((String) -> Void)? = nil
    #endif
}
L.og?("Howdy!")
print("Done.")

Что делает компилятор Swift в версии выпуска этого кода со строкой L.og?("Howdy!")? Это полностью оптимизирует линию? Или выражение L.og оценивается как nil во время выполнения? Как мне доказать любой ответ?

Что я сделал, чтобы попытаться ответить на этот вопрос сам

Я создал крошечную программу, состоящую только из кода, который вы видите выше, и построил ее, используя debugи release конфигурации с swift build -c. Затем я использовал objdump, чтобы разобрать оба файла. В отладочной сборке я вижу сообщение «Howdy», как и ожидалось. Однако я недостаточно знаю сборку и архитектуру macOS, чтобы доказать отсутствие «Howdy» в сборке релиза.

Затем я бросил код L.og в более крупный проект iOS, установил схему Запустите действие Конфигурация сборки с настройкой «Отпустить», установите точку останова на линии L.og, запустите приложение, и когда точка останова достигла, я нажал кнопку «Перейти в» вНавигатор отладки Xcode для перехода в строку L.og, и, как и ожидалось, отладчик Xcode / lldb не перешел в L.og, но это не доказывает, что такое содержимое двоичного файла.

The Документы Swift по «Дополнительному связыванию» не обсуждают эту техническую деталь.

Пояснения

Предположим, что типичная среда сборки, в которой флаг компилятора -DDEBUG установлен только наdebug конфигурации и что не установлено в конфигурации release.

Ответы [ 2 ]

2 голосов
/ 18 октября 2019

Я изменил пример программы на это:

struct L {
    #if DEBUG
    static let og:((String) -> Void)? = { print($0) }
    #else
    static let og:((String) -> Void)? = nil
    #endif
}
L.og?("Howdy!")
let a = 0

Я создаю программу с swift build -c debug и снова с swift build -c release.

Ниже приведена таблица символов отладки. Моя командная строка терминала - «" », и любой текст после нее до новой строки - это то, что я набрал.

?  objdump -macho -indirect-symbols  ./.build/x86_64-apple-macosx/debug/swiftHW 
./.build/x86_64-apple-macosx/debug/swiftHW:
Indirect symbols for (__TEXT,__stubs) 8 entries
address            index name
0x0000000100000ec2    75 _$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC
0x0000000100000ec8    77 _$ss27_allocateUninitializedArrayySayxG_BptBwlFyp_Tg5
0x0000000100000ece    78 _$ss5print_9separator10terminatoryypd_S2StF
0x0000000100000ed4    80 _swift_bridgeObjectRelease
0x0000000100000eda    81 _swift_bridgeObjectRetain
0x0000000100000ee0    82 _swift_once
0x0000000100000ee6    83 _swift_release
0x0000000100000eec    84 _swift_retain
Indirect symbols for (__DATA,__nl_symbol_ptr) 2 entries
address            index name
0x0000000100001000    85 dyld_stub_binder
0x0000000100001008 ABSOLUTE
Indirect symbols for (__DATA,__got) 1 entries
address            index name
0x0000000100001010    76 _$sSSN
Indirect symbols for (__DATA,__la_symbol_ptr) 8 entries
address            index name
0x0000000100001018    75 _$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC
0x0000000100001020    77 _$ss27_allocateUninitializedArrayySayxG_BptBwlFyp_Tg5
0x0000000100001028    78 _$ss5print_9separator10terminatoryypd_S2StF
0x0000000100001030    80 _swift_bridgeObjectRelease
0x0000000100001038    81 _swift_bridgeObjectRetain
0x0000000100001040    82 _swift_once
0x0000000100001048    83 _swift_release
0x0000000100001050    84 _swift_retain

Вот таблица символов выпуска:

?  objdump -macho -indirect-symbols  ./.build/x86_64-apple-macosx/release/swiftHW  
./.build/x86_64-apple-macosx/release/swiftHW:
Indirect symbols for (__TEXT,__stubs) 3 entries
address            index name
0x0000000100000f14    12 _swift_once
0x0000000100000f1a    13 _swift_release
0x0000000100000f20    14 _swift_retain
Indirect symbols for (__DATA,__nl_symbol_ptr) 2 entries
address            index name
0x0000000100001000    15 dyld_stub_binder
0x0000000100001008 ABSOLUTE
Indirect symbols for (__DATA,__la_symbol_ptr) 3 entries
address            index name
0x0000000100001010    12 _swift_once
0x0000000100001018    13 _swift_release
0x0000000100001020    14 _swift_retain

Обратите внимание, чтосимволы для печати (_$ss5print_9separator10terminatoryypd_S2StF) не включены. Я совершенно уверен, что это означает, что компилятор оптимизировал удаление строки L.og?("Howdy!") из двоичного файла выпуска.

0 голосов
/ 18 октября 2019

При условной компиляции компилятор оценивает состояние флага во время сборки.

Код внутри блока #if, который оценивается как true, компилируется, и код, который находится внутри блока #if, которыйоценивается как ложное игнорируется. (Он удаляется и не будет отображаться в двоичном файле.) В вашем случае для сборки выпуска это будет выглядеть так, как если бы ваш код был:

struct L {
    static let og:((String) -> Void)? = nil
}
L.og?("Howdy!")
print("Done.")

Таким образом, «необязательная цепочка» вL.og?("Howdy!") оценил бы L.og как ноль, и этот код никогда бы ничего не сделал.

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

...