post Image
SwiftコンパイラのManglingの勉強方法

SwiftコンパイラのManglingについて勉強するときは、
下記のアプローチがあります。

  1. ドキュメントを読む
  2. デマングルしてみる
  3. Manglerのコードを読む
  4. Demanglerのコードを読む

以降, 個別に詳細を説明します。

ドキュメントを読む

幸いなことに、Manglingについてはドキュメントがあるので、
これを読むと良いでしょう。

https://github.com/apple/swift/blob/master/docs/ABI/Mangling.rst

デマングルしてみる

swift demangle というコマンドで、デマングルすることができます。

$ swift demangle '$S1a3CatC4nameSSvg'
$S1a3CatC4nameSSvg ---> a.Cat.name.getter : Swift.String

-expand オプションを使うと、構造もわかります。

$ swift demangle -expand '$S1a3CatC4nameSSvg'
Demangling for $S1a3CatC4nameSSvg
kind=Global
  kind=Getter
    kind=Variable
      kind=Class
        kind=Module, text="a"
        kind=Identifier, text="Cat"
      kind=Identifier, text="name"
      kind=Type
        kind=Structure
          kind=Module, text="Swift"
          kind=Identifier, text="String"
$S1a3CatC4nameSSvg ---> a.Cat.name.getter : Swift.String

Manglerのコードを読む

Manglerのコードを読んだり、
実際にマングルする処理をデバッガで追いかけてみるとわかりやすいです。

Manglerは下記の2つがあります。

ASTMangler
https://github.com/apple/swift/blob/master/include/swift/AST/ASTMangler.h

IRGenMangler
https://github.com/apple/swift/blob/master/lib/IRGen/IRGenMangler.h

IRGenManglerはASTManglerを継承して、機能を追加しています。

SILGenステージではASTManglerが使用されていて、
IRGenステージではIRgenManglerが使用されています。

例えば、メソッド名などはSILでMangling済みなので、
ASTManglerにメソッドがあります。

std::string ASTMangler::mangleEntity(const ValueDecl *decl, bool isCurried,
                                     SymbolKind SKind)

型名そのものや、メタタイプなどについては、
IRGenステージで必要になるので、IRGenManglerにメソッドがあります。

std::string IRGenMangler::mangleTypeForLLVMTypeName(CanType Ty)

std::string IRGenMangler::mangleTypeMetadataFull(Type type)

Demanglerのコードを読む

Demanglerのコードを読んだり、
実際にデマングルする処理をデバッガで追いかけてみるとわかりやすいです。

swift demangle コマンドのソースは、swift-demangle.cpp です。
https://github.com/apple/swift/blob/master/tools/swift-demangle/swift-demangle.cpp

デマングルを利用するインターフェースは、swift::Demangle::Contextです。
https://github.com/apple/swift/blob/master/include/swift/Demangling/Demangle.h

swift demangle コマンドでは、ユーザから与えられたシンボルを、
下記メソッドに渡すようになっています。

NodePointer Context::demangleSymbolAsNode(llvm::StringRef MangledName)

余談

Demanglerは下位互換性が必要なので、過去の仕様が垣間見えます。
下記のコードから、prefixが変更されてきた様子がわかります。

int swift::Demangle::getManglingPrefixLength(llvm::StringRef mangledName) {
  if (mangledName.empty()) return 0;

  llvm::StringRef prefixes[] = {
    /*Swift 4*/   "_T0",
    /*Swift 4.x*/ "$S", "_$S",
    /*Swift 5+*/  "$s", "_$s"};

  // Look for any of the known prefixes
  for (auto prefix : prefixes) {
    if (mangledName.startswith(prefix))
      return prefix.size();
  }

  return 0;
}

bool swift::Demangle::isOldFunctionTypeMangling(llvm::StringRef mangledName) {
  return mangledName.startswith("_T");
}

SwiftはABIが安定していないので、
マングリングルールが変わるときは、
prefixごと変えてしまうことで、
バージョン番号のように機能させていたのだと思います。

そして最近、swiftコンパイラでは、Mangler側のprefixが $s になりました。
https://github.com/apple/swift/pull/19388

Swift5ではABIが安定化することが予定されているので、
それがうまくいけば、以降は $s から変更されることはないでしょう。


『 Swift 』Article List