Xcode
LLVM
iOS9
bitcode

LLVM bitcode基礎知識

More than 3 years have 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/