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のビルドフローは、ざっくり次のような手順で行われます。
- ソースコードを中間表現(LLVM IR)に変換
- LLVM IRのレベルで最適化
- ターゲット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/