Мертвые функции
SILOptimizer компилятора имеет этап устранения мертвых функций , который удаляет функции и методы, о которых известно, что они не вызываются (вместе со всеми связанными записями таблицы vtable / witness).
Чтобы в полной мере воспользоваться этим, вы захотите использовать оптимизацию всего модуля (-wmo
), чтобы компилятор мог анализировать, являются ли функции internal
вызывается или нет из одного и того же модуля.
Вы можете легко проверить это сами, например, используя следующий код:
class C {}
extension C {
@inline(never) func foo() {}
@inline(never) func bar() {}
}
@inline(never) func foo() {}
@inline(never) func bar() {}
bar()
let c = C()
c.bar()
(я используюнеофициальный @inline(never)
здесь, чтобы гарантировать, что функции не будут оптимизированы путем вставки)
Если мы запустим xcrun swiftc -emit-sil -O -wmo main.swift | xcrun swift-demangle
, мы увидим сгенерированный SIL:
sil_stage canonical
import Builtin
import Swift
import SwiftShims
class C {
init()
deinit
}
extension C {
@inline(never) func foo()
@inline(never) func bar()
}
@inline(never) func foo()
@inline(never) func bar()
let c: C
// c
sil_global hidden [let] @main.c : main.C : $C
// main
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
// function_ref bar()
%2 = function_ref @main.bar() -> () : $@convention(thin) () -> () // user: %3
%3 = apply %2() : $@convention(thin) () -> ()
alloc_global @main.c : main.C // id: %4
%5 = global_addr @main.c : main.C : $*C // user: %8
%6 = alloc_ref $C // users: %8, %7
debug_value %6 : $C, let, name "self", argno 1 // id: %7
store %6 to %5 : $*C // id: %8
// function_ref specialized C.bar()
%9 = function_ref @function signature specialization <Arg[0] = Dead> of main.C.bar() -> () : $@convention(thin) () -> () // user: %10
%10 = apply %9() : $@convention(thin) () -> ()
%11 = integer_literal $Builtin.Int32, 0 // user: %12
%12 = struct $Int32 (%11 : $Builtin.Int32) // user: %13
return %12 : $Int32 // id: %13
} // end sil function 'main'
// C.__deallocating_deinit
sil hidden @main.C.__deallocating_deinit : $@convention(method) (@owned C) -> () {
// %0 // users: %3, %2, %1
bb0(%0 : $C):
debug_value %0 : $C, let, name "self", argno 1 // id: %1
debug_value %0 : $C, let, name "self", argno 1 // id: %2
dealloc_ref %0 : $C // id: %3
%4 = tuple () // user: %5
return %4 : $() // id: %5
} // end sil function 'main.C.__deallocating_deinit'
<b>// bar()
sil hidden [noinline] @main.bar() -> () : $@convention(thin) () -> () {
bb0:
%0 = tuple () // user: %1
return %0 : $() // id: %1
} // end sil function 'main.bar() -> ()'
// specialized C.bar()
sil shared [noinline] @function signature specialization <Arg[0] = Dead> of main.C.bar() -> () : $@convention(thin) () -> () {
bb0:
%0 = tuple () // user: %1
return %0 : $() // id: %1
} // end sil function 'function signature specialization <Arg[0] = Dead> of main.C.bar() -> ()'</b>
sil_vtable C {
#C.deinit!deallocator: @main.C.__deallocating_deinit // C.__deallocating_deinit
}
YouОтметим, что только тело и метод bar
излучали свои тела (около дна).Хотя все еще есть определения для foo
в верхней части SIL, они удаляются, когда SIL понижается до IR LLVM.
Мне было интересно, могли бы вы разметить / приписать методговоря: не удаляйте это!как вы можете на других языках
В настоящее время нет официального атрибута для этого, но есть подчеркнутый атрибут @_optimize(none)
, который говорит оптимизатору не трогать что-либо:
@_optimize(none) func foo() {}
Хотя данный атрибут подчеркнут, используйте его на свой страх и риск.
Метаданные мертвого типа
К сожалению, в настоящее время компилятор (начиная с Swift 4.2) не Покажите , чтобы иметь проход оптимизации, который исключает связанные метаданные для типов, которые, как известно, не используются.