Xcode
LLVM
iOS9
bitcode

LLVM bitcode基礎知識

More than 1 year has passed since last update.

iOS9からのアプリ最適化の仕組みAppThinningのため、Xcode7 betaからはアプリへのbitcodeの埋め込みがデフォルトで有効になりました。

https://developer.apple.com/library/prerelease/ios/documentation/IDEs/Conceptual/AppDistributionGuide/AppThinning/AppThinning.html
http://qiita.com/usagimaru/items/cb19f283db4ac0cd8bd6

bitcodeとは何かを感じ取るためのエントリです。

続き
Xcode7でのembed-bitcodeオプション http://qiita.com/gamako/items/4ebfd048c5aed4f68595

bitcodeとは

LLVMのビルドフローは、ざっくり次のような手順で行われます。

  1. ソースコードを中間表現(LLVM IR)に変換
  2. LLVM IRのレベルで最適化
  3. ターゲットcpuのアセンブリコードに変換

bitcodeはLLVM IRをバイナリファイルに収めるためのファイルフォーマットです。

bitcodeにさわってみよう

簡単なコードでテストしてみます。

Hello Worldなcのソースコードを用意します。

#include <stdio.h>

int main() {
    printf("Hello, World\n");

    return 0;
}

実行ファイルを出力して、実行できることを確認します。できました。

% clang sample.c
% ./a.out
Hello, World

cをLLVM IRに変換

LLVM IRを出力してみる
(オプション-cは無くてもエラーしなかった。)

% clang -c -S -emit-llvm sample.c

出力されたLLVM IRファイルを出力してみると、以下のようなテキストであることがわかります

% cat sample.ll
; ModuleID = 'sample.c'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"

@.str = private unnamed_addr constant [14 x i8] c"Hello, World\0A\00", align 1

; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1
  %2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = metadata !{metadata !"Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)"}

cをbitcodeに変換する

また、bitcode出力するには次のようにします。

% clang -c -emit-llvm sample.c

sample.bcというファイルが出力されている。中身はバイナリなのでダンプ出力してみます。

% hexdump -C sample.bc
00000000  de c0 17 0b 00 00 00 00  14 00 00 00 30 05 00 00  |............0...|
00000010  07 00 00 01 42 43 c0 de  21 0c 00 00 49 01 00 00  |....BC..!...I...|
00000020  0b 82 20 00 02 00 00 00  12 00 00 00 07 81 23 91  |.. ...........#.|

...

00000530  0c 83 11 01 01 00 23 06  04 00 18 42 16 00 00 00  |......#....B....|
00000540  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000550

bitcodeをLLVM IRに変換

bitcodeファイルはllvm-disコマンドでテキストのLLVM IRとして出力することができます。

% /usr/local/Cellar/llvm/3.6.1/bin/llvm-dis sample.bc
% cat sample.ll
; ModuleID = 'sample.bc'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.10.0"

@.str = private unnamed_addr constant [14 x i8] c"Hello, World\0A\00", align 1

; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1
  %2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([14 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { nounwind ssp uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = !{!"Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)"}

bitcodeを直接実行

bitcodeファイルはlliコマンドで直接実行できます

% /usr/local/Cellar/llvm/3.6.1/bin/lli sample.bc
Hello, World

bitcodeから実行ファイルを作成

llcコマンドでターゲットCPUのアセンブラを出力し、これをリンクして実行ファイルをつくることもできます。

% /usr/local/Cellar/llvm/3.6.1/bin/llc sample.bc
% cat sample.s
    .section    __TEXT,__text,regular,pure_instructions
    .macosx_version_min 10, 10
    .globl  _main
    .align  4, 0x90
_main:                                  ## @main
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    leaq    L_.str(%rip), %rdi
    xorl    %eax, %eax
    callq   _printf
    xorl    %eax, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc

    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "Hello, World\n"


.subsections_via_symbols
% clang sample.s
% ./a.out
Hello, World

bitcodeをcppに変更

さらに、llcコマンドは出力アーキテクチャにcppと指定すると、cppソースコードに変換してくれるようです。

見てのとおり、LLVMのフレームワークをつかって実現する形になるようなので簡単に読める感じではありませんが、注意深く見れば"Hello World"文字列を引数に与えて外部のprintf関数を呼び出しているのが読み取れるような、、、
(元ソースがprintfの呼び出しだけだったので、かえって類似性を見つけるのが難しくなってしまいました。)

本エントリはここまでです。

% /usr/local/Cellar/llvm/3.6.1/bin/llc -march=cpp -o sample1.cpp sample1.bc
% cat sample1.cpp
// Generated by llvm2cpp - DO NOT MODIFY!

#include <llvm/Pass.h>
#include <llvm/PassManager.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/CallingConv.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/IRPrintingPasses.h>
#include <llvm/IR/InlineAsm.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/FormattedStream.h>
#include <llvm/Support/MathExtras.h>
#include <algorithm>
using namespace llvm;

Module* makeLLVMModule();

int main(int argc, char**argv) {
  Module* Mod = makeLLVMModule();
  verifyModule(*Mod, PrintMessageAction);
  PassManager PM;
  PM.add(createPrintModulePass(&outs()));
  PM.run(*Mod);
  return 0;
}


Module* makeLLVMModule() {
 // Module Construction
 Module* mod = new Module("sample1.bc", getGlobalContext());
 mod->setDataLayout("0x7fc65a4080b0");
 mod->setTargetTriple("x86_64-apple-macosx10.10.0");

 // Type Definitions
 ArrayType* ArrayTy_0 = ArrayType::get(IntegerType::get(mod->getContext(), 8), 14);

 PointerType* PointerTy_1 = PointerType::get(ArrayTy_0, 0);

 std::vector<Type*>FuncTy_2_args;
 FunctionType* FuncTy_2 = FunctionType::get(
  /*Result=*/IntegerType::get(mod->getContext(), 32),
  /*Params=*/FuncTy_2_args,
  /*isVarArg=*/false);

 PointerType* PointerTy_3 = PointerType::get(IntegerType::get(mod->getContext(), 32), 0);

 PointerType* PointerTy_4 = PointerType::get(IntegerType::get(mod->getContext(), 8), 0);

 std::vector<Type*>FuncTy_6_args;
 FuncTy_6_args.push_back(PointerTy_4);
 FunctionType* FuncTy_6 = FunctionType::get(
  /*Result=*/IntegerType::get(mod->getContext(), 32),
  /*Params=*/FuncTy_6_args,
  /*isVarArg=*/true);

 PointerType* PointerTy_5 = PointerType::get(FuncTy_6, 0);


 // Function Declarations

 Function* func_main = mod->getFunction("main");
 if (!func_main) {
 func_main = Function::Create(
  /*Type=*/FuncTy_2,
  /*Linkage=*/GlobalValue::ExternalLinkage,
  /*Name=*/"main", mod);
 func_main->setCallingConv(CallingConv::C);
 }
 AttributeSet func_main_PAL;
 {
  SmallVector<AttributeSet, 4> Attrs;
  AttributeSet PAS;
   {
    AttrBuilder B;
    B.addAttribute(Attribute::NoUnwind);
    B.addAttribute(Attribute::StackProtect);
    B.addAttribute(Attribute::UWTable);
    PAS = AttributeSet::get(mod->getContext(), ~0U, B);
   }

  Attrs.push_back(PAS);
  func_main_PAL = AttributeSet::get(mod->getContext(), Attrs);

 }
 func_main->setAttributes(func_main_PAL);

 Function* func_printf = mod->getFunction("printf");
 if (!func_printf) {
 func_printf = Function::Create(
  /*Type=*/FuncTy_6,
  /*Linkage=*/GlobalValue::ExternalLinkage,
  /*Name=*/"printf", mod); // (external, no body)
 func_printf->setCallingConv(CallingConv::C);
 }
 AttributeSet func_printf_PAL;
 {
  SmallVector<AttributeSet, 4> Attrs;
  AttributeSet PAS;
   {
    AttrBuilder B;
    PAS = AttributeSet::get(mod->getContext(), ~0U, B);
   }

  Attrs.push_back(PAS);
  func_printf_PAL = AttributeSet::get(mod->getContext(), Attrs);

 }
 func_printf->setAttributes(func_printf_PAL);

 // Global Variable Declarations


 GlobalVariable* gvar_array__str = new GlobalVariable(/*Module=*/*mod,
 /*Type=*/ArrayTy_0,
 /*isConstant=*/true,
 /*Linkage=*/GlobalValue::PrivateLinkage,
 /*Initializer=*/0, // has initializer, specified below
 /*Name=*/".str");
 gvar_array__str->setAlignment(1);

 // Constant Definitions
 Constant *const_array_7 = ConstantDataArray::getString(mod->getContext(), "Hello, World\x0A", true);
 ConstantInt* const_int32_8 = ConstantInt::get(mod->getContext(), APInt(32, StringRef("1"), 10));
 ConstantInt* const_int32_9 = ConstantInt::get(mod->getContext(), APInt(32, StringRef("0"), 10));
 std::vector<Constant*> const_ptr_10_indices;
 const_ptr_10_indices.push_back(const_int32_9);
 const_ptr_10_indices.push_back(const_int32_9);
 Constant* const_ptr_10 = ConstantExpr::getGetElementPtr(gvar_array__str, const_ptr_10_indices);

 // Global Variable Definitions
 gvar_array__str->setInitializer(const_array_7);

 // Function Definitions

 // Function: main (func_main)
 {

  BasicBlock* label_11 = BasicBlock::Create(mod->getContext(), "",func_main,0);

  // Block  (label_11)
  AllocaInst* ptr_12 = new AllocaInst(IntegerType::get(mod->getContext(), 32), "", label_11);
  ptr_12->setAlignment(4);
  StoreInst* void_13 = new StoreInst(const_int32_9, ptr_12, false, label_11);
  CallInst* int32_14 = CallInst::Create(func_printf, const_ptr_10, "", label_11);
  int32_14->setCallingConv(CallingConv::C);
  int32_14->setTailCall(false);
  AttributeSet int32_14_PAL;
  int32_14->setAttributes(int32_14_PAL);

  ReturnInst::Create(mod->getContext(), const_int32_9, label_11);

 }

 return mod;
}

参考

https://blog.ymyzk.com/2015/07/clang-llvm-ir-assembly/
http://llamerad-jp.hatenablog.com/entry/2015/05/14/072225
http://stackoverflow.com/questions/14107743/llvm-and-compiler-nomenclature
http://d.hatena.ne.jp/Akiva/20101220/1292881596
http://d.hatena.ne.jp/wagavulin/20110910/1315620620
http://www.ibm.com/developerworks/jp/opensource/library/os-createcompilerllvm1/