コンパイラ 2012 年 11 月 12 日

18
コココココ 2012 コ 11 コ 12 コ コココ一@A468 ([email protected] ) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/COMP/ 2012/ index.html 1

description

コンパイラ 2012 年 11 月 12 日. 酒居敬一@A468 ( [email protected] ) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/COMP/2012/index.html. コンパイラと実行環境の連携1. OS プロセスやファイルシステムなど仮想化したハードウェアを提供 プログラミング言語に依存しない汎用的なインターフェース 実行環境 プログラミング言語固有のサービス メモリ管理(たとえば、割り付け・自動開放) スレッド管理(生成・消滅・同期・相互排除) - PowerPoint PPT Presentation

Transcript of コンパイラ 2012 年 11 月 12 日

Page 2: コンパイラ 2012 年 11 月 12 日

コンパイラと実行環境の連携1 OS

プロセスやファイルシステムなど仮想化したハードウェアを提供

プログラミング言語に依存しない汎用的なインターフェース 実行環境

プログラミング言語固有のサービス メモリ管理(たとえば、割り付け・自動開放) スレッド管理(生成・消滅・同期・相互排除) 初期化処理

C言語の場合、この初期化処理だけで、限定的に動かすことができる ライブラリ

明示的に利用するもの・暗黙的に利用されるもの コンパイラと同時に開発する必要があるモノ

2

Page 3: コンパイラ 2012 年 11 月 12 日

実行環境

3

ソースコード

オブジェクトコード

コンパイラ

OS

リンク ライブラリ

実行環境

システムコール

Page 4: コンパイラ 2012 年 11 月 12 日

実行環境とコンパイラ たとえば、 lookUpReturnAddressInException()

例外検査コードを生成するとしても、いちいち表を調べるコードをオブジェクトコードに入れておくことは無駄である。通常はコンパイラが、そういったサブルーチンを呼ぶ。

たとえば、キャスト。 実行時の型検査、例外生成あるいは型変換、といった一連

の手続きを、いちいちコード生成しててはメモリを消費する。こういったものも、コンパイラがサブルーチンを呼ぶ。

実行時に呼ばれる、そのような様々なサブルーチンも実行環境であり、コンパイラとペアで開発する。

4

Page 5: コンパイラ 2012 年 11 月 12 日

Garbage Collection ( ごみ集め ) メモリ割り付けと開放の自動化・機械化

Lisp では、メモリは自動で割り付けられ自動で開放される 割り付けはオブジェクト生成時に行われる 使われていないオブジェクトのメモリは、ころあいを見て開放される

Javaでは、開放だけが機械化されている。 割り付けは new オペレータで、明示的に行う。

Cでは、メモリ割り付け・開放については機械化されていない。 malloc()/free() という、ライブラリ関数があるのみ。

メモリの開放に関する問題 開放忘れ:使用可能なメモリの不足による停止 二重開放:メモリ内容の意図せぬ変更によるプログラムの暴走

。 不要メモリを機械的に回収し開放する。→ GC

5

Page 6: コンパイラ 2012 年 11 月 12 日

Java 実行環境

6

Javac (静的コンパイラ)

OS

Java 実行環境

ソースコード

クラスファイル

その他•is_instance_of() など

メモリ管理•newInstance() など

スレッド管理•EnterSynchronizedBlock()•ExitSynchronizedBlock() など

java コマンド( Java VM Emulator )

クラスローダ

動的コンパイラ

インタプリタ

例外処理•暗黙の null 検査、スタックバンギング用シグナルハンドラ•lookUpReturnAddressInException() など

ソースコードソースコード

クラスファイルクラス

ファイル

標準クラスライブラリ•java.lang.System クラス•java.io.orintStream クラス

C言語の極端な例では、スタックポインタを設定し main() を呼ぶだけの初期化処理でもいい。実行環境として、機械語で数命令書けば動くということ。

コンパイラ・アセンブラ・リンケージエディタ、といったコマンドだけでよい例は、

学群実験でやった。

Java は、C言語より楽だ。でも実行環境は相応に大きく複雑になる。

Page 7: コンパイラ 2012 年 11 月 12 日

静的コンパイラ プログラムを実行する前にコンパイルするコンパイ

ラ 普通のコンパイラ。

Java では、利用者の書いたプログラムをコンパイルして得た、 Java VM 向けのバイトコードが出力される。 class ファイルが生成される。

java コマンド実行により、次の処理が行われる。 Java VM の初期化。 引数の class ファイルを読み、 main() を探す。 class loader により、外部参照を解決する。

class ファイルや、 jar ファイルを決められた順で探す。

7

Page 8: コンパイラ 2012 年 11 月 12 日

動的コンパイラ Java VM はエミュレーションなので、やはり遅い

native code に静的コンパイルしないことで、 class ファイル(オブジェクトファイル)を機種非依存にできたことは利点。

そこで、必要に応じて、 javac (Java VM emulator) で、バイトコードを native code に翻訳する。

バイトコードと native code を切り替えながら実行する。

ちなみに、 native code 実装したメソッドなどを、java プログラムから呼び出す手段がある。 混ぜて実行できるくらいなので、当然といえば当然。

8

Page 9: コンパイラ 2012 年 11 月 12 日

標準クラスライブラリ 実行環境が用意する標準的なライブラリ群

GUI bean Java 固有 数学 ネット I/O セキュリティ text 処理 ユーティリティ

9

Page 10: コンパイラ 2012 年 11 月 12 日

Write once, run anywhere にも例外がある ずーっと前の情報工学実験

Java から Webカメラを使って画像をキャプチャしていた。 もちろん標準クラスライブラリにはキャプチャ用クラスはな

い。 当然ながら、OS経由でカメラを直接操作するクラスがない。

実は Java にも、非標準的実装方法ある。 もちろん方法は標準化してある。

JNI (Java Native Interface) 無いものは仕方ないという状況で、どうするか?

あきらめてもらう。 Java は使ってもらえなくなるかもしれない。

プログラマに非標準的実装方法を提供する。 Java VM コードではなく、 java VM が動作する環境のコードで「実装」。

10

Page 11: コンパイラ 2012 年 11 月 12 日

11

public class WebCam {public WebCam() {

if(initializeWebCam("/dev/video0", 640, 480)){throw new java.lang.IllegalArgumentException();

}}public WebCam(String device) {

if(initializeWebCam(device, 640, 480)){throw new java.lang.IllegalArgumentException();

}}public WebCam(int width, int height) {

if(initializeWebCam("/dev/video0", width, height)){throw new java.lang.IllegalArgumentException();

}}public WebCam(String device, int width, int height) {

if(initializeWebCam(device, width, height)){throw new java.lang.IllegalArgumentException();

}}

private static native boolean initializeWebCam(String device, int width, int height);public static native void finalizeWebCam();public static native int getWidth();public static native int getHeight();public static native int[] grabPixels(int[] array);public static native byte[] grabPixels(byte[] array);static {

System.loadLibrary("WebCam");}

}

Page 12: コンパイラ 2012 年 11 月 12 日

12

#include <jni.h>#include "WebCam.h"

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>

#include <sys/types.h>#include <sys/mman.h>#include <sys/fcntl.h>#include <sys/ioctl.h>#include <time.h>

#include <linux/videodev.h>

#ifdef USE_MMX#include <mmintrin.h>#endif

#define SYNC_TIMEOUT 1

/* device stuff */static int bt848, gb_grab, gb_sync, even, start;static char *gb_frame;static int *gb_work;static struct video_mbuf gb_buffers;static struct video_mmap gb_even, gb_odd;

/* geometry stuff */static int cols, rows, pxls;

/* * Class: WebCam * Method: getWidth * Signature: ()I */JNIEXPORT jint JNICALLJava_WebCam_getWidth(JNIEnv *env, jclass obj){

return cols;}

/* * Class: WebCam * Method: getHeight * Signature: ()I */JNIEXPORT jint JNICALLJava_WebCam_getHeight(JNIEnv *env, jclass obj){

return rows;}

#ifdef USE_MMXstatic voidYUV420PtoYRGB(unsigned char *dest, unsigned char *src){

int i, j;unsigned char *yp0, *yp1, *dp0, *dp1;unsigned char *up, *vp;int vu;__m64 gb, yr, yrgb, yyyy0, yyyy1, vuvu;short int vu_gb[4], vu_yr[4];

vu_gb[0] = 454;vu_gb[1] = 0;vu_gb[2] = -88;vu_gb[3] = -183;

vu_yr[0] = 0;vu_yr[1] = 359;vu_yr[2] = 0;vu_yr[3] = 0;

up = src + pxls;vp = up + pxls/4;yp1 = src;dp1 = dest;for(i = 0; i < rows; i+=2){

yp0 = yp1;yp1 = yp0 + cols;dp0 = dp1;dp1 = dp0 + 4*cols;

for(j = 0; j < cols; j+=2){vu = (*vp++ - 128) << 16; /* v */vu |= (*up++ - 128) & 0x0000FFFF; /* u */vuvu = _mm_cvtsi32_si64(vu);vuvu = _mm_unpacklo_pi32(vuvu, vuvu);

gb = _mm_srai_pi32(_mm_madd_pi16(vuvu, *((__m64 *)vu_gb)), 8);yr = _mm_srai_pi32(_mm_madd_pi16(vuvu, *((__m64 *)vu_yr)), 8);yrgb = _mm_packs_pi32(gb, yr);

yyyy0 = _mm_unpacklo_pi8(*((__m64 *)yp0), _mm_setzero_si64());yyyy0 = yyyy1 = _mm_unpacklo_pi16(yyyy0, yyyy0);yyyy0 = _mm_add_pi16(_mm_unpacklo_pi32(yyyy0, yyyy0), yrgb);yyyy1 = _mm_add_pi16(_mm_unpackhi_pi32(yyyy1, yyyy1), yrgb);yyyy0 = _mm_packs_pu16(yyyy0, yyyy1);*((__m64 *)dp0) = yyyy0;yp0 += 2;dp0+= 8;

yyyy0 = _mm_unpacklo_pi8(*((__m64 *)yp1), _mm_setzero_si64());yyyy0 = yyyy1 = _mm_unpacklo_pi16(yyyy0, yyyy0);yyyy0 = _mm_add_pi16(_mm_unpacklo_pi32(yyyy0, yyyy0), yrgb);yyyy1 = _mm_add_pi16(_mm_unpackhi_pi32(yyyy1, yyyy1), yrgb);yyyy0 = _mm_packs_pu16(yyyy0, yyyy1);*((__m64 *)dp1) = yyyy0;yp1 += 2;dp1+= 8;

}}_mm_empty();

}#elsestatic voidYUV420PtoYRGB(unsigned char *dest, unsigned char *src){

int i, j;unsigned char *yp0, *yp1, *dp0, *dp1;unsigned char *up, *vp;int u, v;int r, g, b;

up = src + pxls;vp = up + pxls/4;for(i = 0; i < rows; i+=2){

yp0 = src;src += cols;yp1 = src;src += cols;

dp0 = dest;dest += 4*cols;dp1 = dest;dest += 4*cols;

for(j = 0; j < cols; j+=2){u = *up++ - 128;v = *vp++ - 128;

r = (359*v) >> 8;g = (-88*u - 183*v) >> 8;b = (454*u) >> 8;dp0[0] = ((b + yp0[0]) > 255)? 255: ((b + yp0[0]) < 0)? 0: (b + yp0[0]);dp0[1] = ((g + yp0[0]) > 255)? 255: ((g + yp0[0]) < 0)? 0: (g + yp0[0]);dp0[2] = ((r + yp0[0]) > 255)? 255: ((r + yp0[0]) < 0)? 0: (r + yp0[0]);dp0[3] = yp0[0];

dp0[4] = ((b + yp0[1]) > 255)? 255: ((b + yp0[1]) < 0)? 0: (b + yp0[1]);dp0[5] = ((g + yp0[1]) > 255)? 255: ((g + yp0[1]) < 0)? 0: (g + yp0[1]);dp0[6] = ((r + yp0[1]) > 255)? 255: ((r + yp0[1]) < 0)? 0: (r + yp0[1]);dp0[7] = yp0[1];

dp1[0] = ((b + yp1[0]) > 255)? 255: ((b + yp1[0]) < 0)? 0: (b + yp1[0]);dp1[1] = ((g + yp1[0]) > 255)? 255: ((g + yp1[0]) < 0)? 0: (g + yp1[0]);dp1[2] = ((r + yp1[0]) > 255)? 255: ((r + yp1[0]) < 0)? 0: (r + yp1[0]);dp1[3] = yp1[0];

dp1[4] = ((b + yp1[1]) > 255)? 255: ((b + yp1[1]) < 0)? 0: (b + yp1[1]);dp1[5] = ((g + yp1[1]) > 255)? 255: ((g + yp1[1]) < 0)? 0: (g + yp1[1]);dp1[6] = ((r + yp1[1]) > 255)? 255: ((r + yp1[1]) < 0)? 0: (r + yp1[1]);dp1[7] = yp1[1];

yp0 += 2;yp1 += 2;dp0+= 8;dp1+= 8;

}}

}#endif

static intgrab_queue(struct video_mmap *gb){

if (-1 == ioctl(bt848,VIDIOCMCAPTURE,gb)) {perror("ioctl VIDIOCMCAPTURE");return -1;

}gb_grab++;

return 0;}

static intgrab_wait(struct video_mmap *gb){

int ret = 0;

alarm(SYNC_TIMEOUT);if (-1 == ioctl(bt848,VIDIOCSYNC,&(gb->frame))) {

perror("ioctl VIDIOCSYNC");ret = -1;

}gb_sync++;alarm(0);

return ret;}

/* * Class: WebCam * Method: grabPixels * Signature: ([I)[I */JNIEXPORT jintArray JNICALLJava_WebCam_grabPixels___3I(JNIEnv *env, jclass obj, jintArray array){

if((*env)->GetArrayLength(env, array) < pxls){return NULL;

}if (gb_grab == gb_sync){

/* started */start = clock();if (-1 == grab_queue(even ? &gb_even : &gb_odd)){

return NULL;}

}

if (-1 == grab_queue(even ? &gb_odd : &gb_even)){return NULL;

}

grab_wait(even ? &gb_even : &gb_odd);even = !even;

YUV420PtoYRGB((unsigned char *)gb_work, (gb_frame + gb_buffers.offsets[even ? 1 : 0]));(*env)->SetIntArrayRegion(env, array, 0, pxls, gb_work);return array;

}

/* * Class: WebCam * Method: grabPixels * Signature: ([B)[B */JNIEXPORT jbyteArray JNICALLJava_WebCam_grabPixels___3B(JNIEnv *env, jclass obj, jbyteArray array){

if((*env)->GetArrayLength(env, array) < pxls*3/2){return NULL;

}if (gb_grab == gb_sync){

/* started */if (-1 == grab_queue(even ? &gb_even : &gb_odd)){

return NULL;}

}

if (-1 == grab_queue(even ? &gb_odd : &gb_even)){return NULL;

}

grab_wait(even ? &gb_even : &gb_odd);even = !even;

(*env)->SetByteArrayRegion(env, array, 0, pxls*3/2, (gb_frame + gb_buffers.offsets[even ? 1 : 0]));return array;

}

/* * Class: WebCam * Method: finalizeWebCam * Signature: ()V */JNIEXPORT void JNICALLJava_WebCam_finalizeWebCam(JNIEnv *env, jclass obj){

double elapsed;if (gb_grab > gb_sync){

grab_wait(even ? &gb_even : &gb_odd);}elapsed = (double)(clock() - start)/CLOCKS_PER_SEC;munmap(gb_work, (pxls*4 + 4095) & ~4095);munmap(gb_frame, gb_buffers.size);close(bt848);gb_sync--;

fprintf(stderr, "%d frames grabbed in %.1f seconds(%.1f[fps]).\n",gb_sync, elapsed, (double)gb_sync/elapsed);

gb_sync = gb_grab = even = 0;}

static intinitialize(void){

struct video_capability vcap;struct video_channel vchan;struct video_picture vpic;

/* find video capture card */if(ioctl(bt848, VIDIOCGCAP, &vcap)){

fprintf(stderr, "Can't get video capability.\n");return 1;

}fprintf(stderr, "Video capture card is \"%s\".\n"

"Width: %d - %d, Height: %d - %d\n",vcap.name, vcap.minwidth, vcap.maxwidth, vcap.minheight, vcap.maxheight);

if((cols & 1) || (rows & 1)|| (vcap.minwidth > cols) || (cols > vcap.maxwidth)|| (vcap.minheight > rows) || (rows > vcap.maxheight)){return 1;

}

for(vchan.channel = 0; vchan.channel < vcap.channels; vchan.channel++){if(ioctl(bt848, VIDIOCGCHAN, &vchan)){

fprintf(stderr, "Can't get channel info.\n");return 1;

}if(vchan.flags & VIDEO_VC_TUNER){

continue;}if(vchan.type & VIDEO_TYPE_CAMERA){

vchan.norm = VIDEO_MODE_NTSC;ioctl(bt848, VIDIOCSCHAN, &vchan);fprintf(stderr, "Video source is set to \"%s\".\n", vchan.name);break;

}}if(vchan.channel == vcap.channels){

fprintf(stderr, "Can't find camera.\n");return 1;

}

/* set picture */vpic.brightness = vpic.hue = vpic.colour = vpic.contrast = vpic.whiteness = 32768;vpic.depth = 12;vpic.palette = VIDEO_PALETTE_YUV420P;if(ioctl(bt848, VIDIOCSPICT, &vpic)){

fprintf(stderr, "Can't set YUV420P.\n");return 1;

}

gb_even.frame = 0;gb_odd.frame = 1;gb_odd.format = gb_even.format = vpic.palette;gb_odd.width = gb_even.width = cols;gb_odd.height = gb_even.height = rows;

/* map grab buffer */if (-1 == ioctl(bt848,VIDIOCGMBUF,&gb_buffers)) {

perror("ioctl VIDIOCGMBUF");return 1;

}gb_frame = mmap(0,gb_buffers.size,PROT_READ|PROT_WRITE,MAP_SHARED,bt848,0);if ((char*)-1 == gb_frame) {

perror("mmap");return 1;

} fprintf(stderr, "%d buffers starting at %p. Frame buffer size is %d.\n",

gb_buffers.frames, gb_frame + gb_buffers.offsets[0],(gb_buffers.offsets[1] - gb_buffers.offsets[0]));

/* map working area */gb_work = mmap(0, (pxls*4 + 4095) & ~4095,

PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);if((int *)-1 == gb_work){

perror("mmap");munmap(gb_frame, gb_buffers.size);return 1;

}return 0;

}

/* * Class: WebCam * Method: initializeWebCam * Signature: (Ljava/lang/String;II)Z */JNIEXPORT jboolean JNICALL Java_WebCam_initializeWebCam(JNIEnv *env, jclass obj, jstring device, jint width, jint height){

const jbyte *str;

cols = width;rows = height;pxls = cols*rows;

/* open video camera device */str = (*env)->GetStringUTFChars(env, device, 0);bt848 = open(str, O_RDWR);

if (bt848 < 0) {fprintf(stderr, "failed to open device %s\n", str);(*env)->ReleaseStringUTFChars(env, device, str);return JNI_TRUE;

}(*env)->ReleaseStringUTFChars(env, device, str);

if(initialize()){close(bt848);return JNI_TRUE;

}return JNI_FALSE;

}

Page 13: コンパイラ 2012 年 11 月 12 日

メモリ管理 プログラムの実行に必要なメモリ領域の確保・管理

・開放 OSの上で動く場合

OSへのメモリ要求 brk() ・ sbrk() や valloc() や mmap() システムコール

獲得したメモリの Java プログラムへの割り付け・回収 OSへのメモリ返却

13

メモリ管理方式

比較項目 手動

自動

実行時スタック ごみ集め

適用範囲 広い 狭い 広い

除去困難なバグを生成する確率 大 なし なし

オーバーヘッド 小 小 大

Page 14: コンパイラ 2012 年 11 月 12 日

実行時スタック 関数やメソッド内の局所変数領域として使われる

エントリー時に確保、 return 時に開放。 スタックポインタだけでメモリ管理機構が実装できる

たいていは、プロセッサによるサポートがある。 特定のレジスタがスタックポインタになってる。 スタックフレームを形成・破棄する機械語命令がある。 スタックポインタの操作だけなのでオーバーヘッドが少ない。

コンパイラがスタック操作コードを生成するので開放ミスはない

原理的に静的な割り付けができない ポインタで参照してても、 return 後には必ず消滅する。

14

Page 15: コンパイラ 2012 年 11 月 12 日

15

メソッド呼び出し

メソッドからの返戻

空き領域

newInstance() のスタックフレーム

Example8_methos2()の

スタックフレームExample8_method1()の

スタックフレームExample8_main() の

スタックフレーム

スタックポインタ (TOS)

変数 object の内容を保存する場所

※ object 変数はスタックフレームに置かれてますが、 object 変数が指す本体はヒープに置かれている。

Page 16: コンパイラ 2012 年 11 月 12 日

Heap (ヒープ ) 静的データ・実行時スタック以外のメモリ領域

静的データの一部をあらかじめ割り付ける方法もある たいていはOSから随時割り当てられた仮想記憶領域

OSからはページングサイズで割り当てられたりして使いにくい ヒープを使う方法に3とおりある。

オブジェクトへの割り付けも開放も手動 C言語の malloc()/free() が典型例

オブジェクトへの割り付けは手動、開放は自動 Java の new 開放はGCが安全に機械的に行う

割り付けも開放も自動 Lisp や Scheme など

16

Page 17: コンパイラ 2012 年 11 月 12 日

手動によるメモリ管理 うまく書ければ最高の効率でメモリが管理できる?

必要なときに確保し、不要になった時点で即開放すれば、メモリ領域は効率よく再利用されていいかもしれない。

現実には… 間違いなく free() することはとても面倒

フローグラフ上で、 free() する位置が異なるブロックに現れる malloc() コードと対応する free() を常に同時に記述・抹消する

メモリ割り当てが成功したかどうかの検査も面倒 メモリ割り当て失敗はプログラマの責任じゃないでしょう。

オブジェクトの性質に応じたメモリの割り当ての区別が面倒 スクラッチパッド用の細かなデータ領域 行列演算用領域 フレームバッファ用領域

17

Page 18: コンパイラ 2012 年 11 月 12 日

Garbage Collection の必要性 割り付けはオブジェクトが生成されるとき、という明確

なタイミングがある。一方で開放には、そこまで明確なタイミングがない。

free() に関する次の問題をGCが解決する 開放忘れ

(短期的には)ヒープを圧迫する OSからメモリを一方的に獲得し続ける結果、システム不安定をきた

す 誤開放

使用中のエリアを開放して、再利用されたら暴走しかねない 二重開放

開放済み領域を再び開放したら、ヒープ管理が破綻しかねない

18