● JSライブラリ
JavaScript で書かれたライブラリと直接リンクすることができます。
// lib.js
mergeInto( LibraryManager.library, {
print: function(x){
console.log( x );
}
});
// main.cpp
extern "C" {
void print( int x );
}
int main()
{
print( 12345 ); // これ
return 0;
}
ビルド手順
emcc main.cpp --js-library lib.js -o main.html
C言語のリンケージに含まれるので、extern 宣言するだけで呼び出せます。
Emscription の library*.js を見ると、標準で用意された関数も
JavaScript で書かれていることがわかります。
利点としては Native な JavaScript の方が自由度が高いことが挙げられます。
ただ C/C++ で書いた場合は asm.js 対応コードに変換してくれるため、
どちらが効率がよく動作できるのかは処理内容に依存すると思われます。
● 引数
// lib.js
mergeInto( LibraryManager.library, {
print_char : function(x){
console.log( x );
},
print_short : function(x){
console.log( x );
},
print_int : function(x){
console.log( x );
},
print_float : function(x){
console.log( x );
},
print_double : function(x){
console.log( x );
},
print_llong : function(x,y){
console.log( y * 4294967296 + x );
},
print_string : function(x){
console.log( Pointer_stringify(x) );
}
});
// main.cpp
extern "C" {
void print_char( char x );
void print_short( short x );
void print_int( int x );
void print_float( float x );
void print_double( double x );
void print_llong( long long x );
void print_string( const char* x );
};
int main()
{
print_char( 123 );
print_short( 12345 );
print_int( 123456789 );
print_float( 1.2345f );
print_double( 1.2345678901234 );
print_llong( 12345678901234ll );
print_string( "ABCDEFG" );
return 0;
}
// output 123 12345 123456789 1.2345000505447388 1.2345678901234 12345678901234 ABCDEFG
ポインタを受け取る場合はメモリアクセスが必要です。
型に応じて HEAP8,HEAP16,HEAP32,HEAP64 を用いるか
setValue()/getValue() 命令を使うことができます。
文字列の Object 変換は Pointer_stringify() で可能。
double に収まらない long long (64bit int) 型は、
2個の整数 (low, high) に分解されていることもわかります。
● 戻り値
// lib.js
mergeInto( LibraryManager.library, {
ret_llong : function(x,y){
asm.setTempRet0( 1 ); // high
return 0>>>0; // low
},
});
// main.cpp
#include
extern "C" {
long long ret_llong( long long x );
}
int main()
{
printf( "%lld\n", ret_llong( 0 ) );
return 0;
}
// output 4294967296
64bit 型は global な戻り値レジスタ tempRet0~ を併用しています。
他の関数呼び出しで破壊される可能性があるので atomic ではない点に注意。
文字列定数など static なポインタを返すのは簡単ではないようです。
メインメモリ空間に配置されていなければならないためで、
static な data 領域はコンパイル時に作られます。
ライブラリ初期化時に HEAP から確保して代用する形になるでしょう。
C言語に返すことが可能な heap memory の確保は、
Module._malloc() / Module._free() を使用します。
C言語の runtime に含まれるので、コンパイル時にこれらの関数への参照がなければ
正しいコードが生成されない点に注意が必要です。
// lib.js
mergeInto( LibraryManager.library, {
alloc_int : function(size){
return Module._malloc( size * 4 );
},
dump_int : function(addr, size){
for( i= 0 ; i< size ; i++ ){
console.log( Module.HEAP32[(addr>>2) + i] ); // memory アクセス
}
},
});
// main.cpp
#include
extern "C" {
int* alloc_int( int size );
void dump_int( const int* buffer, int size );
}
int main()
{
int* buffer= alloc_int( 100 );
for( int i= 0 ; i< 100 ; i++ ){
buffer[i]= i * i;
}
dump_int( buffer, 100 );
free( buffer ); // -- (1)
return 0;
}
↑(1) の free が無ければ C言語で malloc()/free() が生成されないため、
JavaScript 側の Module._malloc() は正しい挙動になりません。
例えば下記のようにアロケータを完全に置き換えようとしても、
C言語から参照がないので _malloc/_free がダミーコードになります。
一見正しく動作しますがメモリリークします。
// lib.js
mergeInto( LibraryManager.library, {
my_alloc : function(byte_size){
return Module._malloc( byte_size );
},
my_free : function(addr){
Module._free( addr );
},
});
// main.cpp
extern "C" {
void* my_alloc( int byte_size );
void my_free( void* addr );
}
int main()
{
int* buffer= (int*)my_alloc( 100 * sizeof(int) );
for( int i= 0 ; i< 100 ; i++ ){
buffer[i]= i * i;
}
my_free( buffer );
return 0;
}
● メモリマップ
0~7 NULL trap 領域 STATIC_BASE~ data/bss segument (8~) STACK_BASE~ stack 領域 DYNAMIC_BASE~ heap 領域 ~TOTAL_MEMORY-1
NULL アクセスはコンパイラが検出できる場合だけ例外が発生します。
動的な NULL アクセスはデフォルトではエラーになりませんが、
-s SAFE_HEAP=1 を付けることでメモリアクセス時のアドレスチェックが可能です。
その分動作速度は低下します。
stack は前方から後方に向かって確保されています。
文字列定数や static/global に確保したメモリは STATIC エリアに格納します。
初期値を持たない配列も含まれるため、下記のようなケースでは初期化データに
大量の 0 が並ぶことになります。
ファイルサイズが増加するので、HEAP から確保した方が無駄が少なく済むようです。
// main.cpp
int buffer[1024];
↓
/* memory initializer */ allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
~
次回: Emscripten C++/OpenGL ES 2.0 (6) Chrome の速度と IE11/Safari
関連エントリ
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (4)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (3)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (2)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす (1)
・Emscripten C++/OpenGL ES 2.0 のアプリケーションをブラウザで動かす 一覧