(強いAI)技.術的特.異点/シ.ンギュラリティ158
■ このスレッドは過去ログ倉庫に格納されています
2045年頃に人類は技術的特異点(Technological Singularity)を迎えると予測されている。
未来技術によって、どのような世界が構築されるのか?人類はどうなるのか?
などを様々な視点から網羅的に考察し意見交換する総合的なスレッド
■技術的特異点:収穫加速の法則とコンピュータの成長率に基づいて予測された、
生物的制約から開放された知能[機械ベース or 機械で拡張]が生み出す、
具体的予測の困難な時代が到来する起点
■収穫加速の法則:進歩のペースがどんどん早くなるという統計的法則
ここでの進歩とは、技術的進歩だけでなく生物的進化、生化学的秩序形成も含む
★ 関連スレ(特化した話はこちらで)
(AI) 技術的特異点と政治・経済・社会 (BI)
http://goo☆.gl/riKAbq
(情報科学) 技術的特異点と科学・技術 (ナノテク)
http://goo☆.gl/RqNDAU
※URL部分をコピーし、☆を消してペースト※
※前スレ
(強いAI)技術的特異点/シンギュラリティ でスレタイ検索
https://find.5ch.net/search?q=%28%E5%BC%B7%E3%81%84AI%29%E6%8A%80%E8%A1%93%E7%9A%84%E7%89%B9%E7%95%B0%E7%82%B9%2F%E3%82%B7%E3%83%B3%E3%82%AE%E3%83%A5%E3%83%A9%E3%83%AA%E3%83%86%E3%82%A3 齊藤先生メソッド頓挫 ≒ 飢餓 ( 非 BI ルート )
http://rio2016.2ch.net/test/read.cgi/future/1489922543/111-139##(111,138-139)# SaitouSenseiMesoddo NanoKeizai 4. Subleq用Cコンパイラ
? ry するコンパイラ ry 。
、簡単なCコードをSubleq [11]にコンパイルする自前コンパイラのいくつかの要素について簡単に説明
? ry テストの1つで、コンパイル ry 。
ラは私たちのテストの 1 つで用い、同じ C ソースからコンパイルされたネイティブ C コードと Subleq コードとの実行の間で直接比較が可能
? The compiler is a high-level language interface to OISC ?
? ry コンパイラ ry インタフェースですか? 私たちに知られている唯一のこのようなコンパイラは、執筆の時点で。
そのコンパイラはOISCの高水準言語インタフェースです - その様なコンパイラとして執筆時点で私たちに知られている唯一の。
4.1 スタック
? ry 主要なCプログラミング言語の概念は、 ry 。
C プログラミング言語の主要な概念は、関数とスタック
? ry は、コードの下のメモリを使用することで実現 ry 。
Subleqでのスタックの実装は、下記コード的なメモリ使用で実現でき
? ryの自己修正を使用 ry 、スタックの値に配置して取得することができます。
コードの自己書換を使用すると、スタックに置いたりその値から復元 できます。
関数呼び出しでは、リターンアドレスをスタックに配置 要 。
以下のCコードを考えてみましょう: void f()
{
...
}
void main()
{
...
}
f();
...
}
?8?
Page 9
上記をマシンコードにコンパイルした後、次の操作を実行 要
? 1)fを呼び出した直後 ry 。
1)f を呼出す直後の命令のアドレスをスタックに置かなければならな
2)関数fのコードへジャンプ 要
3)関数fの終わりに、スタックからのアドレスを抽出 要
4)抽出されたアドレスに実行を転送 要
? C標準によれば、 ry 。
C標準に従い、関数mainは適切なC関数です。すなわち、それ自身を含む他の関数から呼び出 できます。
したがって、プログラムには別のエントリポイント 要 。このエントリポイントは、次のコードではsqmainと呼ばれます。
上記のCコードは次のようにコンパイルされます: 0 0 sqmain
_f:
...
#return
?+8; sp ?+4; ?+7; 0 ?+3; Z Z 0
_main:
...
#call f
dec sp; ?+11; sp ?+7; ?+6; sp ?+2; 0
?+6; sp ?+2; ?+2 0 _f; . ?; inc sp
...
#return
?+8; sp ?+4; ?+7; 0 ?+3; Z Z 0
sqmain:
#call main
dec sp; ?+11; sp ?+7; ?+6; sp ?+2; 0
?+6; sp ?+2; ?+2 0 _main; . ?; inc sp
0 0 (-1)
. inc:-1 Z:0 dec:1 sp:-sp
セルスタックポインタspは、プログラム内の最後のメモリセル 。
それは、それ自身のアドレスの負の値で初期化
? A negative value is used here to speed up the code execution ?
? ry 化しますか?
ここで負の値を使用してコード実行を高速化します - データが実際の値の負数として記録されている場合、減算操作を使用すると、いくつかのステップを節約することがあります。
命令dec spはspから1を引くので、その実際の値は1だけ増
? Below is an excerpt calling the function f in more readable form ? relative references ?
? ry 、関数fをより読みやすい形で呼び出す抜粋です。 相対的な参照? ラベルに置き換えられます。
以下は、関数 f を呼出すより読み易い形 - 相対参照? ラベルに置換えられます - の抜粋です。 dec sp
A; sp A
B; sp B
A:0 B:0
C; sp C
D C:0 _f
. D:?
inc sp
? ry クリアすることです。これは、前回の使用時に残っていた値があるためです。
4行目の命令は、スタック内のセルをクリアする為で、前回の使用時に値を残した事に因ります
? しかし、スタック内の最上位セルをクリア ry 。
しかしながら、スタック内のトップセルをクリア は、命令自体のオペランドをクリアしてからspポインタの値で初期化 必要があるため、単一ステップのタスクではありませ
? ry です。
したがって、実行コマンドシーケンスは次のとおりです :
スタックポインタを増加させることによってスタック内に新しいセルを割り当てる(第1行)。
命令の第1オペランドをクリア 。 このオペランドをスタックポインタの新しい値で初期化する(2行目
? do the same with the second operand of the instruction ?
? ry 同じことをしますか?
命令の第2オペランドと同じことをします - クリアして初期化する(3行目)。
? その命令を実行すると、スタック内の割り当てられたセル ry 。
そしてそれからこの命令を実行すると、スタック内のアロケートされたセルがクリアされます(4行目
? 次の2つの命令は、 ry 。
同じ様に次の 2 つの命令は、セルCをクリアして初期化
? The instruction DC:0 _f ry .
The instruction D C:0 _f ry .
? 命令D C:0_fは、 ry 。
命令 D C:0 _f は、命令inc spのアドレスをスタックにコピーし、_ fにジャンプ
? これは、Dが次 ry 保持しているため( ry )、Cはスタック ry 指しているためです。
これが機能するのは、Dが次のメモリセルの値を保持している(覚えていますか?)、そして C がスタック上の現在消去されているトップセルを指しているのが理由 。 http://rio2016.2ch.net/test/read.cgi/future/1555604755/60 ?9?
Page 10
? ry 、ラベル_fにジャンプします。
スタックに書き込まれた負の値は、ラベル _f へのジャンプを強制します。 >>67
関数fの内部では、スタックポインタを変更することができますが、関数が終了する前にそれを復元すると仮定 。
したがって、リターンコードはスタックから抽出されたアドレスにジャンプしなければなりません:
A; sp A
B; A:0 B
Z Z B:0
ここでスタックポインタspの値はAに書き込まれ、命令A:0 Bは格納されたアドレスをBにコピーする
? ry が負に格納され、 ry 。
アドレスが負で格納され、正の値が復元されています。
? ry だけではありません。
スタックはリターンアドレスを格納するだけという訳ではありません。
これについては、後のサブセクション4.3と4.4で説明 。 4.2 式
? C言語の操作は、キーワード・ ry と式で ry 。
C 言語のオペレーションは、キーワード・ステートメントと式とで構成されるステートメントで構成
? The syntax of keyword statements and expressions are best represented by Backus-Naur Forms (BNF) ?
? ry (BNF)によって最もよく表現されますか? 文脈自由文法を表す標準的な方法。
キーワードステートメントと式の構文はBackus-Naur Forms(BNF) - 文脈自由文法を表す標準的な方法 - によってベストに表現されます 。
古典的な例は、算術式の文法です:
expression:= 式:=
term ? 期間
expression + term ? 式+用語
expression - term ? 表現 - 用語
term:=
primary 一次
term * primary ? 期間*プライマリ
term / primary ターム/プライマリ
primary:= プライマリ:=
identifier 識別子
constant 定数
( expression ) (表現)
これらの相互に再帰的な定義は、文法的に有効な任意の式のツリー表現を構築するためにparserと呼ばれるプログラムによって使用できます。
? このような ry 役割は、一連の命令を整理 ry 。
ひとたびこのようなツリーが構築されれば、コンパイラの役割は、命令のシーケンスを整理して、すべてのサブツリーの結果がツリーに渡されるようにすること 。
たとえば、式のツリー: a + ( b - c )
? ノード ry 変数a、およびノー??ド「 - 」と変数bおよびcからなるサブツリーからなる。
は、 ノード「+」、変数 a、および、ノード「 - 」と変数 b および c とからなるサブツリー、からなる。
図
a
+
b
-
c
? ry 。これは後でさらに使用 ry 。 ry 。
計算を行うために、コンパイラは、サブツリーの結果を格納するために一時変数を使用 要 。これは後に加算で使用 要 。 この式がより大きな式の一部である場合、潜在的にさらに使用される可能性があります。
? ry では、一時的なものは1つ ry 、 ry の一時的なものが必要です。
この特定の例では、テンポラリは 1 つだけ必要ですが、一般的には多くのテンポラリ 要 。
式は次のコードにコンパイルされます:
t; b Z; Z t; Z
c t
a Z; Z t; Z
?10?
Page 11 最初の行は値bを一時的なtにコピーします。 >>71
2行目は、一時的な値から値cを減算
? ry 、コンパイラはサブツリーで終了します。
この時点で、サブツリーを携えてコンパイラは終了 。
その結果は、生成されたコードと、計算されたサブツリーの値を保持する一時変数t 。
今度はコンパイラが追加用のコードを生成 。
? その引数は変数aと一時tです。
その今度の引数は変数 a とテンポラリ t
? 3行目にaを追加します。
a を t に 3 行目で加算 。
今、tは式全体の結果を保持 。
この式がより大きな式の一部である場合、tはツリーの上位ノードへの引数としてツリーに渡され 。
そうでない場合は、評価が終了したため、t値は破棄
より高度な文法には、代入、逆参照、単項演算など 。
しかし、各文法構造は、対応するサブツリーによって表現され、後にコンパイラによって処理されてコードを生成 できる
? 例えば、Cで表される逆 ry 減算は次のようになります。
例えば、逆参照された値からの減算の C 表記つまり :
*k -= a
? 翻訳されなければならない
は次の様に変換されなければならない
t; k Z; Z t; Z
a t:0
ここでは、間接参照のために一時変数をコード内で使用 要
? 命令の順序は次のとおりです。 kをtにコピーする。 aからkを引く。
命令の手順は : t をクリア。 k を t にコピーする。 k がポイントするメモリから a を引く。 ここでは、文法処理のいくつかの要素が触れられている。
? ry するために数ページしかかかりません。
C文法は、BNFをリストするだけの為に数ページ使います
? ry 文法は、 ry 。
しかし、より大きくより複雑な文法が、同様の方法でコンパイラによって解決される
4.3 関数呼び出し
? ry 、スタックに押し込んでポップ ry 。
上のサブセクション4.1では、スタックにプッシュしポップする方法が示 。
関数が引数を取るとき、それらは戻りアドレスとともにスタックにプッシュされなければならない。
関数の復帰時にスタックを復元 要 。
2つの引数を取る関数を考えてみましょう:
int f(int a, int b);
...
f(a,b);
関数fへの呼び出しは、次のようなものに変換されなければなりません
# 1 push b #1プッシュb
# 2 push a ? #2プッシュ
# 3 push return_address
# 4 goto f
# return_address:
# 5 sp -= 3
? ry 別の式の一部にすることができます。 コンパイラ
Cの引数は式に でき、関数への呼び出しは別の式の一部 - サブ式に できます。即ちコンパイラは次のようなより複雑なケースを適切に処理 要
?11? Page 12
int f(int a, int b)
{
...
return f; fを返す。
}
...
int k;
k=f;
k(f(1,2),3); // call via variable - indirect call //変数経由で呼び出し - 間接呼び出し
k = f(1,2)(3,4); // call by return value //戻り値で呼び出す
ここでは簡単のため、Cの関数型int(*)(int、int)はintとして表さ
? ry は1つの変数タイプのみ ry 。
Subleqは変数の型を 1 つのみサポート
? したがって、より精巧なタイピングシステムは、言語に余分な機能を導入しません。
従って言語に、余分な機能をより精巧なタイピングシステムが齎しません
? ry として適切に計算 ry 。
スタックにプッシュされた引数は、サブ式(サブツリー)として恙なく計算できます
? 実際の関数呼び出しのこの意味では、プログラム変数または一時変数のいずれかがスタック ry されることはありません。
この意味では、プログラム変数又は一時変数、のどちらも実際の関数呼出でスタックにプッシュされるいわれはありません。 # 1 push B #1プッシュB
? # ry [spは負であることを覚えています]
#スタック内の次のセルをクリアする[ sp が負である事を忘れない ]
? #下の行はCの構文と同じです:*(++ sp)= 0;
# *(++sp)=0; という C 構文と下の行とは同じです:
dec sp; t1; sp t1; t2; sp t2; t1:0 t2:0
#Cの構文と同じです:* sp + = B;
t3; sp t3; b Z; Z t3:0; Z
#2プッシュA
? # the same with A ? B ?
#Aと同じ
dec sp; t4; sp t4; t5; sp t5; t4:0 t5:0
t6; sp t6; a Z; Z t6:0; Z
#3 push return_address
dec sp; t7; sp t7; t8; sp t8; t7:0 t8:0
t9; sp t9; t10 t9:0 goto_address
. t10: return_address
#4 goto f
goto_address: Z Z f
#5 sp - = 3
return_address: const(-3) sp
? 表記const(-3)spは
ノート : const(-3) sp は以下の短縮
unique_name sp
...
unique_name:-3 ? ry 扱っていません。
上のコードは戻り値も間接呼び出しもまだ扱っていません。 >>76
戻り値は特別な変数(レジスタ)に格納できます
? ry サブ式でする場合は、戻り値をすぐに一時的に値にコピー ry 。
プログラムがサブ式の戻り値を使用するならば、戻り値を return 後すぐにテンポラリにコピー 要
? ry アドレスを一時的に保持する参照解除によって ry 。
間接呼び出しは、関数のアドレスを保持しているテンポラリを間接参照する事によって実現できます。
それは簡単ですが、より複雑なコード
スタックポインタは、関数がスタック(ローカル)変数を要求するときに関数内で変更できます。
ローカル変数にアクセスするには、通常、ベースポインタbpが使用されま
? 関数の ry 。 ローカル変数の基本参照 ry いますか? 各ローカル変数には、ベースポインタからの関連オフセットがあります。 関数の終わりにスタックポインタを復元するために使用 ry 。
それは関数の入り口で初期化されます。 それはローカル変数のベース参照として使用されています - 各ローカル変数は、ベースポインタから紐付けられたオフセットを持ち 。 そしてそれはスタックポインタ復元の為に関数の終わりで使用されま
? ry できます。つまり、各関数は、終了時のベースポインタの終了時に入力および復元時に保存する必要があります。
関数は他の関数を呼び出すことができ、つまり、各関数が、ベースポインタをエントリ時点で保存しそして終了時に復元 要を意味します
? ry ます。
したがって、関数本体は次のコマンドでラップする必要があります :
?12?
Page 13 1. # push bp 1.#プッシュbp
2. # sp -> bp
3. # sp -= stack_size
# ... function body #...関数本体
5. # bp -> sp
6. # pop bp
7. # return ? 7.#返品
またはSubleqコードで。
dec sp; ?+11; sp ?+7; ?+6; sp ?+2; 0
?+6; sp ?+2; bp 0
bp; sp bp
stack_size sp
# ... function body #...関数本体
sp; bp sp
?+8; sp ?+4; bp; 0 bp; inc sp
?+8; sp ?+4; ?+7; 0 ?+3; Z Z 0 ? ry 、解析中の関数 ry 。
stack_sizeは定数で、パーシングに伴って関数ごとに計算されます >>78
? bp ry 不十分であることが判明しました。
要するに bpを保存するだけでは不十分 。
関数呼び出しは、式の中で起こることがあります
? ry 、表現の一時的なものを保存 ry 。
そのような場合には、式の全てのテンポラリを保存しなければならない
? 新しい機能は、 ry 同じ一時メモリセルを ry 。
新しい関数は、それ自身の必要性のために同じテンポラリメモリセルを使用することになります。
式f()+ g()に対して、呼び出しの結果は変数t1とt2に格納
? ry いる関数gがt1を変更 ry 。
関数fの結果が格納されている t1 を関数 g が変更すると、問題が発生
? ry 、すべての機能を使用しているすべての一時的なデータをスタック ry 。
解決策は、各関数毎にそれが使用している全テンポラリをスタックにプッシュし、終了時にそれらを復元すること 。
以下の関数を考えてみましょう:
int g()
{
return k+1; // k + 1を返す。
}
? ry に翻訳されます:
それは次のようにトランスレートされます: >>79
_g:
# save bp
dec sp; ?+11; sp ?+7; ?+6; sp ?+2; 0
?+6; sp ?+2; bp 0
bp; sp bp
# push t1 #プッシュt1
dec sp; ?+11; sp ?+7; ?+6; sp ?+2; 0
?+6; sp ?+2; t1 0
# push t2 #プッシュt2
dec sp; ?+11; sp ?+7; ?+6; sp ?+2; 0
?+6; sp ?+2; t2 0
# calculate addition #加算を計算する
t1; t2
_k t1
dec t1
t1 t2
# set the return value [negative] #戻り値を設定する[負]
ax; t2 ax ? 斧; t2 ax
# pop t2
?+8; sp ?+4; t2; 0 t2; inc sp
# pop t1
?+8; sp ?+4; t1; 0 t1; inc sp
# restore bp
sp; bp sp
?+8; sp ?+4; bp; 0 bp; inc sp
# exit # 出口
?+8; sp ?+4; ?+7; 0 ?+3; Z Z 0 ?13?
Page 14
コードのどこかに他の関数の呼び出しがある場合、他の関数が実行時にそれらを保存して復元するため、一時変数t1とt2は計算値を保持
? ry すべての一時的なデータはスタック ry されるので、使用された一時的なデータの数を減らすために支出されます。
関数内で使用された全てのテンポラリがスタックにプッシュされる事を以て、使用されるテンポラリの数を減らす為の支払となります。
? これは、使用された一時的なものを、使用された一時的なもののプールに解放することによって、これを行うことができます。
これは、使用されたテンポラリを、使用テンポラリのプールに逃がすだけで可能
? ry 、新しい一時的なものが要求 ry 新しい一時的なものが割り当てられます。
その後、新しいテンポラリが要求されると、プールが最初にチェックされ、そのプールが空の場合にのみ新しいテンポラリがアロケート
? 表現
式 1+k[1]
? コンパイルする
は以下の通りにコンパイルされる
t1; t2; _k t1; dec t1; t1 t2
t3; t4; ?+11; t2 Z; Z ?+4; Z; 0 t3; t3 t4;
t5; t6; dec t5; t4 t5; t5 t6
# result in t6 ? #t6の結果 # 結果は t6 の中
? ry 時間軸のプール ry 、時間軸の数 ry 半分になります。
テンポラリのプールが導入されると、テンポラリの数は半分になり :
t1; t2; _k t1; dec t1; t1 t2
t1; t3; ?+11; t2 Z; Z ?+4; Z; 0 t1; t1 t3
t1; t2; dec t1; t3 t1; t1 t2
# result in t2 ? #結果はt2になります #結果は t2 の中
? ry 削除するコードが ry 。
対応するプッシュおよびポップ操作を削除しコードが大幅に削減
4.4スタック変数
bpがスタックに置かれ、spがデクリメントされてメモリが割り当てられると、すべてのローカル変数が使用可能になります。
コンパイラはアドレスを知らないため間接的にしかアクセスできませ
? 例えば、関数f in
例えば、以下の関数 f int f(int x, int y)
{
int a, b=3, c[3], d=5;
...
}
f(7,9);
? スタックサイズが6に等しい4つのローカル変数があります。
は 6 に等しいスタックサイズと共にある 4 つのローカル変数、を持ちます。
? ry 関数が入力されると、スタックは次の値を ry :
この関数が開始されると、スタックは下記の値を持ちます:
... y[9] x[7] [return_address] [saved_bp] a[?] b[3] c0[?] c1[?] c2[?] d[5] ...
^ ^
(bp) (sp)
コンパイラは、bpからの各変数のオフセットについて知っています。
?14?
Page 15
可変オフセット
y -3
x -2
a 1
b 2
c 3
d 6 したがって、コードでは、配列を指さないローカル変数への参照は*(bp + offset)で置き換 できます
? arrayの ry 。
配列の名前は最初の要素のアドレスなので、配列cは(bp + offset)に置き換えなければなりません。
名前は変数を参照するのではなく、[]で参照することは変数を参照します。
Cで
c[i]
? is the same as ?
is the same as
? と同じです
は以下と同じで
*(c+i)
この例では次のように解釈できます。 >>83
*((bp+3)+i) ■ このスレッドは過去ログ倉庫に格納されています