PostgreSQL 11に搭載されるJITコンパイラ機能を動かしてみる

PostgreSQL 11から、JIT機能が追加されました。

https://www.postgresql.org/about/news/1855/

https://news.mynavi.jp/article/20180324-605157/

あまり理解できていないのですが、どんな感じで動くのか、まずは触ってみたいと思います。

導入と動作確認にあたり、以下の資料を参考にさせていただきました。

https://www.sraoss.co.jp/tech-blog/wp-content/uploads/2018/07/pg11_report-6.pdf

また、llvmについては参考リンクのページに事前に目を通しておくと、どんな事をしているのかイメージしやすいと思います。llvmのtutorialは、ざっと目を通しておくと良いかもしれません。

インストール

試してみた環境は以下です。

  • Mac OS X mac High Sierra 10.13.5
  • iMac (27-inch Late 2012) Intel Core i5 3.2GHz

llvmのインストール

PostgreSQLは、JITの実装としてデフォルトでLLVMを使用するようになっているようです。私の環境は、Homebrewを使用していますので、LLVMを以下のようにしてインストールします。

$ brew install --with-toolchain llvm

参考 src/backend/jit/jit.c

PostgreSQL11beta2のインストール

自作のpgenv2[1]https://github.com/moritoru81/pgenv2でサクッとインストールします。

まず、configureオプションで--with-llvmを追加します。

$ echo "--with-llvm" | pgenv configure -e

続いて11beta2をインストールします。

$ CC="$(brew --prefix llvm)/bin/clang" \
CXX="$(brew --prefix llvm)/bin/clang++" \
CLANG="$(brew --prefix llvm)/bin/clang" \
LLVM_CONFIG="$(brew --prefix llvm)/bin/llvm-config" \
pgenv install -f -v -d 11beta2

11beta2をデフォルト使用にします。

$ pgenv global 11beta2
$ psql -V
psql (PostgreSQL) 11beta2

JITに関連するGUCs

PostgreSQL 11のソースツリーにあるsrc/backend/jit/READMEを参照すると、以下のGUCがjitの動作に関係してくるようです。テストで動作確認したい場合、以下のパラメータに小さなコスト値を設定することで、JITを発動しやすくできます。

jit jitの有効または無効化。デフォルトON(利用可能な場合)
jit_above_cost jitコンパイル適用のコスト閾値。デフォルト100000
jit_optimize_above_cost jitコンパイルされたプログラムを最適化するコスト閾値。デフォルト500000
jit_inline_above_cost jitコンパイルされたプログラムの関数や演算をインライン化するコスト閾値。デフォルト500000

参考 https://www.postgresql.org/docs/11/static/runtime-config-query.html

SET jit TO on;
SET jit_above_cost TO 10;
SET jit_optimize_above_cost TO 10;
SET jit_inline_above_cost TO 10;

参考 

JITを動かしてみる

JITが活きてくるのは、CPUバウンドかつ長時間実行されるような処理、とドキュメントでも説明されています。そうでない場合は、JITコンパイルのオーバヘッドの方が大きくなってしまいます。

まずはJITが動くかクエリを実行してみます。GUCでJITが有効になるよう設定して、適当なクエリを実行してみます。演算式や関数が含まれるクエリが良いでしょう。

SET jit TO on;
SET jit_above_cost TO 10;
SET jit_optimize_above_cost TO 10;
SET jit_inline_above_cost TO 10;

DROP TABLE IF EXISTS t1;

CREATE TABLE t1 (id int PRIMARY KEY, message text);
INSERT INTO t1 SELECT i, md5(i::text) FROM generate_series(1,10000) AS i;
explain analyze SELECT id, substring(message from 2 for 5) as msg FROM t1 WHERE id % 3 = 0;

上記のクエリを実行してみます。explainすると、JIT…という記述があるのが分かります。

$ psql -a -d postgres -f jit-test.sql 
SET jit TO on;
SET
SET jit_above_cost TO 10;
SET
SET jit_optimize_above_cost TO 10;
SET
SET jit_inline_above_cost TO 10;
SET
DROP TABLE IF EXISTS t1;
DROP TABLE
CREATE TABLE t1 (id int PRIMARY KEY, message text);
CREATE TABLE
INSERT INTO t1 SELECT i, md5(i::text) FROM generate_series(1,10000) AS i;
INSERT 0 10000
explain analyze SELECT id, substring(message from 2 for 5) as msg FROM t1 WHERE id % 3 = 0;
                                             QUERY PLAN                                              
-----------------------------------------------------------------------------------------------------
 Seq Scan on t1  (cost=0.00..244.15 rows=53 width=36) (actual time=41.886..43.290 rows=3333 loops=1)
   Filter: ((id % 3) = 0)
   Rows Removed by Filter: 6667
 Planning Time: 0.156 ms
 JIT:
   Functions: 4
   Generation Time: 0.747 ms
   Inlining: true
   Inlining Time: 8.330 ms
   Optimization: true
   Optimization Time: 22.257 ms
   Emission Time: 11.185 ms
 Execution Time: 44.248 ms
(13 rows)

次は、JITコンパイルの効果が出ているのがわかるように、もう少し複雑なクエリを実行してみます。(実行するクエリは、参考資料と同様、実際には大容量のデータの入ったテーブルを準備するのでなく、実行時にタプルを生成するようにしています)

$ psql -a -d postgres -f jit-test2.sql 
SET jit_above_cost TO 0;
SET
SET jit_optimize_above_cost TO 0;
SET
SET jit_inline_above_cost TO 0;
SET
CREATE OR REPLACE FUNCTION bigtable() RETURNS SETOF bigint ROWS 100000000
LANGUAGE sql AS $$
  SELECT i from generate_series(1, 100000000::bigint) AS i;
$$;
CREATE FUNCTION
------------- Test1 -------------
-- JIT off
SET jit TO off;
SET
explain analyze
  SELECT i + 1
  FROM bigtable() AS i;
                                                              QUERY PLAN                                                               
---------------------------------------------------------------------------------------------------------------------------------------
 Function Scan on bigtable i  (cost=0.25..1250000.25 rows=100000000 width=8) (actual time=30235.864..44473.956 rows=100000000 loops=1)
 Planning Time: 0.112 ms
 Execution Time: 49630.957 ms
(3 rows)

-- JIT on
SET jit TO on;
SET
explain analyze
  SELECT i + 1
  FROM bigtable() AS i;
                                                              QUERY PLAN                                                               
---------------------------------------------------------------------------------------------------------------------------------------
 Function Scan on bigtable i  (cost=0.25..1250000.25 rows=100000000 width=8) (actual time=29792.983..42322.049 rows=100000000 loops=1)
 Planning Time: 0.040 ms
 JIT:
   Functions: 2
   Generation Time: 0.414 ms
   Inlining: true
   Inlining Time: 7.773 ms
   Optimization: true
   Optimization Time: 10.477 ms
   Emission Time: 5.095 ms
 Execution Time: 47463.748 ms
(11 rows)

------------- Test2 -------------
-- JIT off
SET jit TO off;
SET
explain analyze
  SELECT i, random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50)))
  FROM bigtable() AS i;
                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Function Scan on bigtable i  (cost=0.25..6000000.25 rows=100000000 width=16) (actual time=29855.820..63744.774 rows=100000000 loops=1)
 Planning Time: 0.055 ms
 Execution Time: 68899.182 ms
(3 rows)

-- JIT on
SET jit TO on;
SET
explain analyze
  SELECT i, random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50)))
  FROM bigtable() AS i;
                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Function Scan on bigtable i  (cost=0.25..6000000.25 rows=100000000 width=16) (actual time=29844.647..55540.690 rows=100000000 loops=1)
 Planning Time: 0.049 ms
 JIT:
   Functions: 2
   Generation Time: 0.685 ms
   Inlining: true
   Inlining Time: 5.877 ms
   Optimization: true
   Optimization Time: 25.856 ms
   Emission Time: 24.207 ms
 Execution Time: 60690.048 ms
(11 rows)

------------- Test3 -------------
-- JIT off
SET jit TO off;
SET
explain analyze
  SELECT (i % 3) AS G, sum(random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50))))
  FROM bigtable() AS i
  WHERE i % 3 = 0
  GROUP BY G;
                                                                QUERY PLAN                                                                
------------------------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=0.25..1528752.75 rows=200 width=16) (actual time=54172.701..54172.701 rows=1 loops=1)
   Group Key: (i % '3'::bigint)
   ->  Function Scan on bigtable i  (cost=0.25..1501250.25 rows=500000 width=16) (actual time=29738.813..43309.330 rows=33333333 loops=1)
         Filter: ((i % '3'::bigint) = 0)
         Rows Removed by Filter: 66666667
 Planning Time: 1.470 ms
 Execution Time: 54232.246 ms
(7 rows)

-- JIT on
SET jit TO on;
SET
explain analyze
  SELECT (i % 3) AS G, sum(random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50))))
  FROM bigtable() AS i
  WHERE i % 3 = 0
  GROUP BY G;
                                                                QUERY PLAN                                                                
------------------------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=0.25..1528752.75 rows=200 width=16) (actual time=48123.079..48123.079 rows=1 loops=1)
   Group Key: (i % '3'::bigint)
   ->  Function Scan on bigtable i  (cost=0.25..1501250.25 rows=500000 width=16) (actual time=30027.104..41229.939 rows=33333333 loops=1)
         Filter: ((i % '3'::bigint) = 0)
         Rows Removed by Filter: 66666667
 Planning Time: 0.089 ms
 JIT:
   Functions: 11
   Generation Time: 1.914 ms
   Inlining: true
   Inlining Time: 12.941 ms
   Optimization: true
   Optimization Time: 67.625 ms
   Emission Time: 44.255 ms
 Execution Time: 48204.660 ms
(15 rows)

私の環境では、結果は以下のようでした(本来は、複数回の計測を行ないばらつきを考慮すべきですが、以下では簡単のため省略しています)

Test クエリ Execution Time (msec)
JIT OFF JIT ON
1 SELECT i + 1 FROM bigtable() AS i; 49630.957 47463.748
2 SELECT i, random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50)))
FROM bigtable() AS i;
68899.182 60690.048
3 SELECT (i % 3) AS G, sum(random() * pi() * exp(i % 2) + log(i % 100 + random() + 1) + sqrt(round(log(i % 100 + 50))))
FROM bigtable() AS i
WHERE i % 3 = 0
GROUP BY G;

 
54232.246 48204.660 

Test1は、単純な加算処理ですが、演算対象の行数が多いためか、JITがONの場合には多少の改善が見られました。

Test2・3では、関数と演算を組み合わせてみましたが、数秒程度の改善が見られました。

厳密なベンチではないので適正な評価はできませんが、どのようなケースでJITが有効か、ざっと雰囲気は掴めたような気がします。

JIT機能を少し追ってみる

『PostgreSQLのJITは、どのような仕組みなのだろうか?』、ざっと雰囲気が掴めたら、次は内部を知りたくなってきます。あまり深いところまで追いきれていませんが、入り口部分を覗いて見ました。

JITプロバイダ

PostgreSQLでは、JIT機能のデフォルト実装はLLVMとなっているが、他のJIT実装も可能なよう設計されている(といっても、私はLLVM以外であまり知りません。。)。

参考 src/backend/jit/jit.c

変数jit_providerで指定される動的ライブラリをロードし、実装ごとの_PG_jit_provider_init関数内でJitProviderCallbacks構造体オブジェクトの関数ポインタに実装ごとの関数のアドレスが登録される。デフォルト実装はLLVMであるが、LLVMの場合はllvmjit.cに_PG_jit_provider_init定義がある。

参考 src/backend/jit/llvm/llvmjit.c

JITプロバイダの初期化は、SQL関数を呼ぶことによって明示的に初期化することもできる。以下を実行すると、backendプロセスで、jit_providerで指定される動的モジュールをロードすることができる。

$ psql
psql (11beta2)
Type "help" for help.

postgres=# set client_min_messages to DEBUG1;
SET
postgres=# select pg_jit_available();
DEBUG:  probing availability of JIT provider at /Users/guests/workspace/github/pgenv2/versions/11beta2/lib/postgresql/llvmjit.so
DEBUG:  successfully loaded JIT provider in current session
 pg_jit_available 
------------------
 t
(1 row)

pg_jit_available関数の呼び出し前後で、lldbで動的ライブラリの適当なシンボルが見えるか確認してみる。以下は、LLVMCreateBuilder関数が見えるかbackendプロセスにアタッチして調べた例である。pg_jit_available関数からprovider_init関数が呼ばれ、llvmjit.soがロードされたことがわかる。

(lldb) image lookup -r -n LLVMCreateBuilder
# provider_init()呼び出し前は、シンボル見つからず
(lldb) image lookup -r -n LLVMCreateBuilder
2 matches found in /Users/guests/workspace/github/pgenv2/versions/11beta2/lib/postgresql/llvmjit.so:
        Address: llvmjit.so[0x0000000000ad3a78] (llvmjit.so.__TEXT.__text + 11347560)
        Summary: llvmjit.so`LLVMCreateBuilder        Address: llvmjit.so[0x0000000000ad3a3c] (llvmjit.so.__TEXT.__text + 11347500)
        Summary: llvmjit.so`LLVMCreateBuilderInContext

JITの対象

PostgreSQLでは、JIT機能はbackendプロセスで動作し、JITコンパイルの対象は以下となっている。

  1. expression evaluation
    1. WHERE句、ターゲットリスト、集約、射影などの式評価
  2. tuple deforming
    1. ディスク上のタプル表現のインメモリ表現への変換

参考 https://www.postgresql.org/docs/11/static/jit-reason.html#JIT-ACCELERATED-OPERATIONS

JIT Contextと生成されるコード

以降はLLVMを前提で記述する。

JITコンパイルのエントリーポイントは、jit_compile_expr関数である。

1.expression evaluationでは、LLVM IRでevalexpr_<module_generation>_<counter>という関数が生成される。module_generationは、LLVMモジュールの生成数(名前はpg)、counterはオブジェクトの出力回数であり、これらの変数はLLVMJitContext構造体で定義されている。

参考 src/backend/jit/llvm/llvmjit_expr.c

2. tuple deformingでは、LLVM IRでdeform_<module_generation>_<counter>という関数が生成される。

参考 src/backend/jit/llvm/llvmjit_deform.c

LLVMJitContextは、plan実行時に作成され、トランザクションの終わりで解放される。

参考 src/backend/executor/execMain.c

1、2で生成されるLLVM IRをみるには、jit_dump_bitcodeパラメータをonにすればよい。パラメータをonにして、JITが働くクエリを実行すると、pgdata下に.bcファイルが生成される。.llで人が読める形式に変換するにはllvm-disを実行する。

試しに以下のようなクエリを実行してみる。

postgres=# set jit_dump_bitcode to on;
SET
postgres=# select abs(-100);
 abs 
-----
 100
(1 row)

私の実行した環境では、以下のファイルが生成された。

  • 90645.14.bc
  • 90645.14.optimized.bc

optimizedは最適化されたビットコードである。90645.14.bcをllvm-disでテキストとして読めるLLVM IR形式に変換してみた結果が以下である。

最初の%で始まる定義は、型定義である。LLVM IRでは、%で始まる名前はローカル識別子、@で始まる名前はグローバル識別子を示している[2]https://llvm.org/docs/LangRef.html#identifiers

$ llvm-dis 90645.14.bc
$ cat 90645.14.ll
; ModuleID = '90645.14.bc'
source_filename = "pg"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.13.0"
%struct.ExprState = type { %struct.Node, i8, i8, i64, %struct.TupleTableSlot*, %struct.ExprEvalStep*, i64 (%struct.ExprState*, %struct.ExprContext*, i8*)*, %struct.Expr*, i8*, i32, i32, %struct.PlanState*, %struct.ParamListInfoData*, i64*, i8*, i64*, i8* }
%struct.Node = type { i32 }
%struct.TupleTableSlot = type { i32, i8, i8, i8, i8, %struct.HeapTupleData*, %struct.tupleDesc*, %struct.MemoryContextData*, i32, i32, i64*, i8*, %struct.MinimalTupleData*, %struct.HeapTupleData, i32, i8 }
%struct.tupleDesc = type { i32, i32, i32, i8, i32, %struct.tupleConstr*, [0 x %struct.FormData_pg_attribute] }
%struct.tupleConstr = type { %struct.attrDefault*, %struct.constrCheck*, %struct.attrMissing*, i16, i16, i8 }
%struct.attrDefault = type { i16, i8* }
%struct.constrCheck = type { i8*, i8*, i8, i8 }
%struct.attrMissing = type opaque
%struct.FormData_pg_attribute = type { i32, %struct.nameData, i32, i32, i16, i16, i32, i32, i32, i8, i8, i8, i8, i8, i8, i8, i8, i8, i32, i32 }
%struct.nameData = type { [64 x i8] }
%struct.MemoryContextData = type { i32, i8, i8, %struct.MemoryContextMethods*, %struct.MemoryContextData*, %struct.MemoryContextData*, %struct.MemoryContextData*, %struct.MemoryContextData*, i8*, i8*, %struct.MemoryContextCallback* }
%struct.MemoryContextMethods = type { i8* (%struct.MemoryContextData*, i64)*, void (%struct.MemoryContextData*, i8*)*, i8* (%struct.MemoryContextData*, i8*, i64)*, void (%struct.MemoryContextData*)*, void (%struct.MemoryContextData*)*, i64 (%struct.MemoryContextData*, i8*)*, i1 (%struct.MemoryContextData*)*, void (%struct.MemoryContextData*, void (%struct.MemoryContextData*, i8*, i8*)*, i8*, %struct.MemoryContextCounters*)* }
%struct.MemoryContextCounters = type { i64, i64, i64, i64 }
%struct.MemoryContextCallback = type { void (i8*)*, i8*, %struct.MemoryContextCallback* }
%struct.MinimalTupleData = type { i32, [6 x i8], i16, i16, i8, [0 x i8] }
%struct.HeapTupleData = type { i32, %struct.ItemPointerData, i32, %struct.HeapTupleHeaderData* }
%struct.ItemPointerData = type { %struct.BlockIdData, i16 }
%struct.BlockIdData = type { i16, i16 }
%struct.HeapTupleHeaderData = type { %union.anon, %struct.ItemPointerData, i16, i16, i8, [0 x i8] }
%union.anon = type { %struct.HeapTupleFields }
%struct.HeapTupleFields = type { i32, i32, %union.anon.0 }
%union.anon.0 = type { i32 }
%struct.ExprEvalStep = type { i64, i64*, i8*, %union.anon.1 }
%union.anon.1 = type { %struct.anon.25 }
%struct.anon.25 = type { i64*, i8*, i32, i32, %struct.FmgrInfo*, %struct.FunctionCallInfoData* }
%struct.FmgrInfo = type { {}*, i32, i16, i8, i8, i8, i8*, %struct.MemoryContextData*, %struct.Node* }
%struct.FunctionCallInfoData = type { %struct.FmgrInfo*, %struct.Node*, %struct.Node*, i32, i8, i16, [100 x i64], [100 x i8] }
%struct.Expr = type { i32 }
%struct.PlanState = type { i32, %struct.Plan*, %struct.EState*, %struct.TupleTableSlot* (%struct.PlanState*)*, %struct.TupleTableSlot* (%struct.PlanState*)*, %struct.Instrumentation*, %struct.WorkerInstrumentation*, %struct.ExprState*, %struct.PlanState*, %struct.PlanState*, %struct.List*, %struct.List*, %struct.Bitmapset*, %struct.TupleTableSlot*, %struct.ExprContext*, %struct.ProjectionInfo*, %struct.tupleDesc* }
%struct.Plan = type { i32, double, double, double, i32, i8, i8, i32, %struct.List*, %struct.List*, %struct.Plan*, %struct.Plan*, %struct.List*, %struct.Bitmapset*, %struct.Bitmapset* }
%struct.EState = type { i32, i32, %struct.SnapshotData*, %struct.SnapshotData*, %struct.List*, %struct.PlannedStmt*, i8*, %struct.JunkFilter*, i32, %struct.ResultRelInfo*, i32, %struct.ResultRelInfo*, %struct.ResultRelInfo*, i32, %struct.List*, %struct.List*, %struct.TupleTableSlot*, %struct.TupleTableSlot*, %struct.TupleTableSlot*, %struct.ParamListInfoData*, %struct.ParamExecData*, %struct.QueryEnvironment*, %struct.MemoryContextData*, %struct.List*, %struct.List*, i64, i32, i32, i32, i8, %struct.List*, %struct.List*, %struct.List*, %struct.ExprContext*, %struct.HeapTupleData**, i8*, i8*, i8, %struct.dsa_area*, i32, %struct.JitContext* }
%struct.SnapshotData = type { i1 (%struct.HeapTupleData*, %struct.SnapshotData*, i32)*, i32, i32, i32*, i32, i32*, i32, i8, i8, i8, i32, i32, i32, i32, %struct.pairingheap_node, i64, i64 }
%struct.pairingheap_node = type { %struct.pairingheap_node*, %struct.pairingheap_node*, %struct.pairingheap_node* }
%struct.PlannedStmt = type { i32, i32, i64, i8, i8, i8, i8, i8, i8, i32, %struct.Plan*, %struct.List*, %struct.List*, %struct.List*, %struct.List*, %struct.List*, %struct.Bitmapset*, %struct.List*, %struct.List*, %struct.List*, %struct.List*, %struct.Node*, i32, i32 }
%struct.JunkFilter = type { i32, %struct.List*, %struct.tupleDesc*, i16*, %struct.TupleTableSlot*, i16 }
%struct.ResultRelInfo = type { i32, i32, %struct.RelationData*, i32, %struct.RelationData**, %struct.IndexInfo**, %struct.TriggerDesc*, %struct.FmgrInfo*, %struct.ExprState**, %struct.Instrumentation*, %struct.FdwRoutine*, i8*, i8, %struct.List*, %struct.List*, %struct.ExprState**, %struct.JunkFilter*, %struct.List*, %struct.ProjectionInfo*, %struct.List*, %struct.OnConflictSetState*, %struct.List*, %struct.ExprState*, %struct.RelationData*, i8 }
%struct.IndexInfo = type { i32, i32, i32, [32 x i16], %struct.List*, %struct.List*, %struct.List*, %struct.ExprState*, i32*, i32*, i16*, i32*, i32*, i16*, i8, i8, i8, i8, i32, i32, i8*, %struct.MemoryContextData* }
%struct.TriggerDesc = type { %struct.Trigger*, i32, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8 }
%struct.Trigger = type { i32, i8*, i32, i16, i8, i8, i32, i32, i32, i8, i8, i16, i16, i16*, i8**, i8*, i8*, i8* }
%struct.FdwRoutine = type opaque
%struct.OnConflictSetState = type { i32, %struct.ProjectionInfo*, %struct.tupleDesc*, %struct.ExprState* }
%struct.RelationData = type opaque
%struct.ParamExecData = type { i8*, i64, i8 }
%struct.QueryEnvironment = type opaque
%struct.dsa_area = type opaque
%struct.JitContext = type opaque
%struct.Instrumentation = type { i8, i8, i8, %struct.timespec, %struct.timespec, double, double, %struct.BufferUsage, double, double, double, double, double, double, double, %struct.BufferUsage }
%struct.timespec = type { i64, i64 }
%struct.BufferUsage = type { i64, i64, i64, i64, i64, i64, i64, i64, i64, i64, %struct.timespec, %struct.timespec }
%struct.WorkerInstrumentation = type { i32, [0 x %struct.Instrumentation] }
%struct.List = type { i32, i32, %struct.ListCell*, %struct.ListCell* }
%struct.ListCell = type { %union.anon.4, %struct.ListCell* }
%union.anon.4 = type { i8* }
%struct.Bitmapset = type { i32, [0 x i32] }
%struct.ProjectionInfo = type { i32, %struct.ExprState, %struct.ExprContext* }
%struct.ParamListInfoData = type { %struct.ParamExternData* (%struct.ParamListInfoData*, i32, i1, %struct.ParamExternData*)*, i8*, void (%struct.ParamListInfoData*, %struct.Param*, %struct.ExprState*, i64*, i8*)*, i8*, void (%struct.ParseState*, i8*)*, i8*, i32, [0 x %struct.ParamExternData] }
%struct.ParamExternData = type { i64, i8, i16, i32 }
%struct.Param = type { %struct.Expr, i32, i32, i32, i32, i32, i32 }
%struct.ParseState = type opaque
%struct.ExprContext = type { i32, %struct.TupleTableSlot*, %struct.TupleTableSlot*, %struct.TupleTableSlot*, %struct.MemoryContextData*, %struct.MemoryContextData*, %struct.ParamExecData*, %struct.ParamListInfoData*, i64*, i8*, i64, i8, i64, i8, %struct.EState*, %struct.ExprContext_CB* }
%struct.ExprContext_CB = type { %struct.ExprContext_CB*, void (i64)*, i64 }
; Function Attrs: norecurse nounwind ssp uwtable
define i64 @evalexpr_14_0(%struct.ExprState*, %struct.ExprContext*, i1*) #0 {
entry:
%v.state.resvalue = getelementptr inbounds %struct.ExprState, %struct.ExprState* %0, i32 0, i32 3
%v.state.resnull = getelementptr inbounds %struct.ExprState, %struct.ExprState* %0, i32 0, i32 2
%3 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 1
%v_scanslot = load %struct.TupleTableSlot*, %struct.TupleTableSlot** %3
%4 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 2
%v_innerslot = load %struct.TupleTableSlot*, %struct.TupleTableSlot** %4
%5 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 3
%v_outerslot = load %struct.TupleTableSlot*, %struct.TupleTableSlot** %5
%6 = getelementptr inbounds %struct.ExprState, %struct.ExprState* %0, i32 0, i32 4
%v_resultslot = load %struct.TupleTableSlot*, %struct.TupleTableSlot** %6
%7 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_scanslot, i32 0, i32 10
%v_scanvalues = load i64*, i64** %7
%8 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_scanslot, i32 0, i32 11
%v_scannulls = load i8*, i8** %8
%9 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_innerslot, i32 0, i32 10
%v_innervalues = load i64*, i64** %9
%10 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_innerslot, i32 0, i32 11
%v_innernulls = load i8*, i8** %10
%11 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_outerslot, i32 0, i32 10
%v_outervalues = load i64*, i64** %11
%12 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_outerslot, i32 0, i32 11
%v_outernulls = load i8*, i8** %12
%13 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_resultslot, i32 0, i32 10
%v_resultvalues = load i64*, i64** %13
%14 = getelementptr inbounds %struct.TupleTableSlot, %struct.TupleTableSlot* %v_resultslot, i32 0, i32 11
%v_resultnulls = load i8*, i8** %14
%15 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 8
%v.econtext.aggvalues = load i64*, i64** %15
%16 = getelementptr inbounds %struct.ExprContext, %struct.ExprContext* %1, i32 0, i32 9
%v.econtext.aggnulls = load i8*, i8** %16
br label %b.op.0.start
b.op.0.start:                                     ; preds = %entry
store i64 100, i64* inttoptr (i64 140524024251152 to i64*)
store i8 0, i8* inttoptr (i64 140524024251149 to i8*)
br label %b.op.1.start
b.op.1.start:                                     ; preds = %b.op.0.start
%17 = load i64, i64* %v.state.resvalue
%18 = load i8, i8* %v.state.resnull
%19 = getelementptr i64, i64* %v_resultvalues, i32 0
%20 = getelementptr i8, i8* %v_resultnulls, i32 0
store i64 %17, i64* %19
store i8 %18, i8* %20
br label %b.op.2.start
b.op.2.start:                                     ; preds = %b.op.1.start
%21 = load i64, i64* %v.state.resvalue
%22 = load i8, i8* %v.state.resnull
%23 = trunc i8 %22 to i1
store i1 %23, i1* %2
ret i64 %21
}
attributes #0 = { norecurse nounwind ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

71行目付近に関数定義があり、evalexpr_xx_xという名前になっていることがわかる。

LLVM IRを生成しているllvm_compile_expr関数をみると、LLVMのIRの構造に沿って、コード作成されていくことがわかる。

LLVMModuleCreateWithNameでModuleを作成し、LLVMAddFunctionでModuleにFunctionを追加し、LLVMAppendBasicBlockでBasicBlockを追加、BuilderでInstructionの構築・・・といった感じである。

参考リンク

 

 

きつねさんでもわかるLLVM ~コンパイラを自作するためのガイドブック~
柏木 餅子 風薬
インプレス
売り上げランキング: 87,944

脚注   [ + ]

byebyehaikikyou

日記やIT系関連のネタ、WordPressに関することなど様々な事柄を書き付けた雑記です。ITエンジニア経験があるのでプログラミングに関することなどが多いです。

シェアする

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

コメントする

Translate »