a21_txt02_7
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
* 「10日くらいでできる!プログラミング言語自作入門」の続...
-(by [[K]], 2021.05.06)
** (1) はじめに
-このテキストは、「10日くらいでできる!プログラミング言語...
-このシリーズでは、言語に新規の命令を追加することは最小限...
-言語に独自の機能を加えていって言語を「進化」させていく話...
** (2) HL-17
-ということで、HL-17について説明したいと思います。これはH...
-HL-17の基本方針はこうです。
--[1]compile()関数では、内部コード出力をやめて、代わりにx...
--[2]exec()関数は不要なので削除。
--[3]機械語を出力するにあたって、putIc()のままでは全然便...
--[4]それにともない、putIc()関数は期待通りには動かなくな...
--[5]とりあえず、単純代入命令とprint命令だけputIcX64()に...
-こうすることで、HL-9aをJITコンパイラ型の言語に改造します...
-この改造をすると、言語はCPUに依存するようになります。''x...
-しかしそれでも、やるだけの価値はあります。それほど高速に...
-説明が前後していますが「JITコンパイラ」というのは「Just-...
#include <acl.c>
typedef unsigned char *String; // こう書くと String は u...
int loadText(String path, String t, int siz) → HL-4と同...
////////////////////////////////////////////////////////...
#define MAX_TC 1000 // トークンコードの最大値.
String ts[MAX_TC + 1]; // トークンの内容(文字列)を記憶.
int tl[MAX_TC + 1]; // トークンの長さ.
unsigned char tcBuf[(MAX_TC + 1) * 10]; // トークン1つ当...
int tcs = 0, tcb = 0;
AInt var[MAX_TC + 1]; // 変数.
int getTc(String s, int len) → HL-8aと同じなので省略
////////////////////////////////////////////////////////...
int isAlphabetOrNumber(unsigned char c) → HL-2と同じなの...
int lexer(String s, int tc[]) → HL-9aと同じなので省略
int tc[10000]; // トークンコード.
enum { TcSemi = 0, TcDot, TcWiCard, Tc0, Tc1, Tc2, Tc3, ...
TcEEq, TcNEq, TcLt, TcGe, TcLe, TcGt, TcPlus, TcMinu...
TcComma, TcExpr, TcExpr0, TcTmp0, TcTmp1, TcTmp2, Tc...
TcSubPrint, TcSubTime, TcSubRgb8, TcSubXorShift, TcS...
TcSubFilRct0, TcSubDrwStr0, TcSubGprDec, TcSubOpnWin...
char tcInit[] = "; . !!* 0 1 2 3 4 5 6 7 8 ( ) [ ] { } =...
"sub_print sub_time sub_aRgb8 sub_xorShift sub_aGetP...
"sub_aFilRct0 sub_aDrwStr0 sub_gprDec sub_opnWin sub...
////////////////////////////////////////////////////////...
int phrCmp_tc[32 * 100], ppc1, wpc[9], wpc1[9]; // ppc1:...
int phrCmp(int pid, String phr, int pc) → HL-7と同じなの...
////////////////////////////////////////////////////////...
#define ABI_MSW64 1 // Windows.
#define ABI_SYSV64 0 // Linuxなど.
typedef AInt *IntP; // こう書くと IntP は AInt * の代わ...
enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpC...
OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, O...
OpPrints, OpAryNew, OpAryInit, OpArySet, OpAryGet, O...
OpXorShift, OpGetPix, OpFilRct0, OpPrm, OpF16Sin, Op...
unsigned char *ic, *icq; // ic[]:内部コード、icq:ic[]へ...
void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3) ...
{
printf("putIc: error\n");
exit(1);
}
int getHex(int c) // 16進数に使える文字ならそれを0~15の...
{
if ('0' <= c && c <= '9') return c - '0';
if ('a' <= c && c <= 'f') return c - 'a' + 10;
if ('A' <= c && c <= 'F') return c - 'A' + 10;
return -1;
}
int get32(unsigned char *p)
{
return p[0] + p[1] * 256 + p[2] * 65536 + p[3] * 167...
}
void put32(unsigned char *p, AInt i)
{
p[0] = i & 0xff; // 1バイト目に、ビット0~7...
p[1] = (i >> 8) & 0xff; // 2バイト目に、ビット8~15...
p[2] = (i >> 16) & 0xff; // 3バイト目に、ビット16~2...
p[3] = (i >> 24) & 0xff; // 4バイト目に、ビット24~3...
}
unsigned char *putIcX64_rex;
void putIcX64_sub(String s, IntP a[])
{
int i, j, k;
for (i = 0; s[i] != 0; ) {
if (s[i] == ' ' || s[i] == '\t' || s[i] == '_' |...
i++;
} else if (getHex(s[i]) >= 0 && getHex(s[i + 1])...
*icq = getHex(s[i]) * 16 + getHex(s[i + 1]);
i += 2;
icq++;
} else if (s[i] == '%') {
if (s[i + 1] == 'R') {
putIcX64_rex = icq;
*icq = 0x48;
icq++;
i += 2;
continue;
}
j = s[i + 1] - '0';
if (s[i + 2] == 'm') { // mod r/m.
k = s[i + 3] - '0';
if (s[i + 3] >= 'a') { // もしかしたらこ...
k = s[i + 3] - 'a' + 10;
}
*icq = 0x85 + (k & 7) * 8;
if (k >= 8) {
*putIcX64_rex |= 4;
}
put32(icq + 1, (a[j] - var) * 8);
icq += 5;
i += 4;
continue;
}
if (s[i + 2] == 'i') { // int.
put32(icq, (AInt) a[j]);
icq += 4;
}
if (s[i + 2] == 'c') { // char.
*icq = (AInt) a[j];
icq++;
}
if (s[i + 2] == 'q') { // long long (qword).
put32(icq, (AInt) a[j]);
put32(icq + 4, ((AInt) a[j]) >> 32);
icq += 8;
}
i += 3;
} else {
printf("putIcX64: error: '%s'\n", s);
exit(1);
}
}
}
void putIcX64(String s, IntP p0, IntP p1, IntP p2, IntP ...
{
IntP a[4];
a[0] = p0;
a[1] = p1;
a[2] = p2;
a[3] = p3;
putIcX64_sub(s, a);
}
////////////////////////////////////////////////////////...
char tmp_flag[10]; // 一時変数の利用状況を管理.
int tmpAlloc() → HL-7と同じなので省略
void tmpFree(int i) → HL-7と同じなので省略
////////////////////////////////////////////////////////...
void sub_print(AInt i)
{
printf("%d\n", (int) i);
}
void initTcSub()
{
var[TcSubPrint] = (AInt) sub_print;
}
////////////////////////////////////////////////////////...
int epc, epc1; // exprのためのpcとpc1.
int exprSub(int priority); // exprSub1()が参照するので、...
int expr(int j);
int phrCmpPutIc(int pid, String phr, int pc, int *pi, in...
int exprSub1(int i, int priority, int op) → HL-7と同じな...
int exprSub(int priority) → HL-9と同じなので省略
int expr(int j) → HL-7と同じなので省略
////////////////////////////////////////////////////////...
enum { IfTrue = 0, IfFalse = 1 };
void ifgoto(int i, int not, int label) → HL-8と同じなの...
int tmpLabelNo;
int tmpLabelAlloc() → HL-8と同じなので省略
#define BInfSiz 10
int binf[BInfSiz * 100], bd, lbd; // binf:block-info, bd...
enum { BlkIf = 1, BlkFor, BlkMain }; // BlkMainをHL-9aで...
enum { IfLabel0 = 1, IfLabel1 };
enum { ForLopBgn = 1, ForCont, ForBrk, ForLbd0, ForWpc01...
int phrCmpPutIc(int pid, String phr, int pc, int *pi, in...
{
if (phrCmp(pid, phr, pc)) {
printf("phrCmpPutIc: error\n");
exit(1);
}
return 0;
}
int phrCmpPutIcX64(int pid, String phr, int pc, int *pi,...
{
int e[9], i;
if (phrCmp(pid, phr, pc)) {
e[0] = e[1] = e[2] = e[3] = e[4] = e[5] = e[6] =...
for (i = 0; i < lenExpr; i++) {
e[i] = expr(i);
}
#if (ABI_MSW64 != 0)
if (lenExpr >= 1) { putIcX64("%R_8b_%0m1;",...
if (lenExpr >= 2) { putIcX64("%R_8b_%0m2;",...
if (lenExpr >= 3) { putIcX64("%R_8b_%0m8;",...
if (lenExpr >= 4) { putIcX64("%R_8b_%0m9;",...
for (i = 4; i < lenExpr; i++) {
putIcX64("%R_8b_%0m0; %R_89_44_24_%1c;",...
}
#elif (ABI_SYSV64 != 0)
if (lenExpr >= 1) { putIcX64("%R_8b_%0m7;",...
if (lenExpr >= 2) { putIcX64("%R_8b_%0m6;",...
if (lenExpr >= 3) { putIcX64("%R_8b_%0m2;",...
if (lenExpr >= 4) { putIcX64("%R_8b_%0m1;",...
if (lenExpr >= 5) { putIcX64("%R_8b_%0m8;",...
if (lenExpr >= 6) { putIcX64("%R_8b_%0m9;",...
for (i = 6; i < lenExpr; i++) {
putIcX64("%R_8b_%0m0; %R_89_44_24_%1c;",...
}
#endif
putIcX64("%R_ff_%0m2;", &var[sub], 0, 0, 0); // ...
for (i = 0; i < lenExpr; i++) {
if (e[i] < 0) {
*err = -1;
}
tmpFree(e[i]);
}
if (pi != 0) {
*pi = tmpAlloc();
putIcX64("%R_89_%0m0;", &var[*pi], 0, 0, 0);...
}
return 1;
}
return 0;
}
////////////////////////////////////////////////////////...
int compile(String s)
{
int pc, pc1, i, j;
! unsigned char *icq1, *icp;
pc1 = lexer(s, tc);
tc[pc1++] = TcSemi; // 末尾に「;」を付け忘れることが...
tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = ...
icq = ic;
+ putIcX64("41_57; 41_56; 41_55; 41_54; 41_53; 41_52;"...
+ putIcX64("41_51; 41_50; 57; 56; 55; 54; 53; 52; 51; ...
+ putIcX64("%R_81_ec_f8_01_00_00; %R_bd_%0q;", var, 0,...
for (i = 0; i < 10; i++) {
tmp_flag[i] = 0;
}
tmpLabelNo = 0;
bd = lbd = 0;
for (pc = 0; pc < pc1; ) { // コンパイル開始.
int e0 = 0, e2 = 0;
if (phrCmp( 1, "!!*0 = !!*1;", pc)) { // 単純代入.
! putIcX86("%R_8b_%1m0; %R_89_%0m0;", &var[tc[...
} else if (phrCmp(10, "!!*0 = !!*1 + 1; if (!!*2...
(中略)
! } else if (phrCmpPutIcX86(4, "print !!**0;", pc,...
(中略)
}
if (bd > 0) {
printf("block nesting error (bd=%d, lbd=%d, pc=%...
return -1;
}
+ putIcX64("%R_81_c4_f8_01_00_00;", 0, 0, 0, 0);
+ putIcX64("58; 59; 5a; 5b; 5c; 5d; 5e; 5f; 41_58; 41_...
! putIcX64("41_5a; 41_5b; 41_5c; 41_5d; 41_5e; 41_5f; ...
icq1 = icq;
// ここにあった「goto先の設定」はいったん全部削除.
return icq1 - ic;
err:
printf("syntax error : %s %s %s %s\n", ts[tc[pc]], t...
return -1;
}
// このあたりにあったexec()関数の記述はすべて削除.
// ついでに変数winの宣言も削除.
int run(String s)
{
if (compile(s) < 0)
return 1;
+ void (*func)() = (void (*)()) ic;
! func();
return 0;
}
// 以下のmallocRWX()はWindows用の記述. Linux系のOSの場合...
#include <windows.h>
void *mallocRWX(int siz)
{
return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE...
}
////////////////////////////////////////////////////////...
void aMain()
{
unsigned char txt[10000];
int i;
+ ic = mallocRWX(1024 * 1024); // 1MB
lexer(tcInit, tc);
+ initTcSub();
if (aArgc >= 2) {
(中略)
}
-プログラムは741行になりました。このHL-17では、printと単...
>print 1
1
>print 2
2
>a=4
>print a
4
-とてもつまらなくなったのではありますが、しかしこれは全部...
** (3) HL-17に関する重要な説明
-このHL-17は、x64の64bitモード専用で書かれています。した...
-そしてこれはHL-11の説明にも書いたことですが、x64の機械語...
** (4) 今回の改造のあらまし
-今回からic[ ]に機械語を入れて実行することになるのですが...
-しかしこれができなければ、そもそもJITコンパイラなんて実...
-Windowsの場合は、上記のプログラムのように
#include <windows.h>
void *mallocRWX(int siz)
{
return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE...
}
-とすることで可能になります。
-LinuxなどのPOSIX系のOSでは、以下のようにやればできます。
#include <unistd.h>
#include <sys/mman.h>
void *mallocRWX(int siz)
{
return mmap(0, siz, PROT_READ | PROT_WRITE | PROT_EX...
}
-HL-17ではこのmallocRWX()を呼び出して、返値をicに代入して...
-ic[ ]の中に機械語を書き込んだ後は、
void (*func)() = (void (*)()) ic;
func();
-これで呼び出しています(これはrun()関数の中に書いてあり...
-2行目はただの関数呼び出しです。こうすることで、ic[ ]に書...
-ic[ ]に適切な機械語を書き込むのは、compile()関数の仕事で...
-なお、ic[ ]に機械語を書き込む際には、1バイトずつ書き込む...
** (5) 生成している機械語の説明(putIcX64()やphrCmpPutIcX...
-ここを読んでいる多くの人は、おそらくx64の機械語なんて知...
----
-まず大事なこととして、x64ではWindowsかそれ以外かで、関数...
#define ABI_MSW64 1 // Windows.
#define ABI_SYSV64 0 // Linuxなど.
-のところで選んでください(上記はWindowsの場合になってい...
-compile()では、ic[ ]の冒頭に、
putIcX64("41_57; 41_56; 41_55; 41_54; 41_53; 41_52;"...
putIcX64("41_51; 41_50; 57; 56; 55; 54; 53; 52; 51; ...
putIcX64("%R_81_ec_f8_01_00_00; %R_bd_%0q;", var, 0,...
-を書きこんでいます。また末尾には、
putIcX64("%R_81_c4_f8_01_00_00;", 0, 0, 0, 0);
putIcX64("58; 59; 5a; 5b; 5c; 5d; 5e; 5f; 41_58; 41_...
putIcX64("41_5a; 41_5b; 41_5c; 41_5d; 41_5e; 41_5f; ...
-を書き込んでいます。・・・まずはこのあたりから説明します。
-まず命令「50」~「57」は、1バイトの命令で、RAX~RDIの8つ...
-・・・こうすることで、プログラム内では16個のレジスタを自...
-命令「%R_81_ec_f8_01_00_00;」は7バイトの命令で、RSP=RSP-...
-そして「%R_bd_%0q;」はRBPレジスタに64bit定数を代入するた...
-ここでputIcX64()関数についても説明します。最初に文字列で...
----
-単純代入命令では、
putIcX64("%R_8b_%1m0; %R_89_%0m0;", &var[tc[wpc[0]]]...
-という機械語を使っています。ちなみにここはHL-9aでは
putIc(OpCpy, &var[tc[wpc[0]]], &var[tc[wpc[1]]], 0,...
-になっていました。第二引数以降は完全に同じになっています...
-命令「%R_8b」と「%R_89」はどちらもアセンブラではMOV命令...
-次の%1m0の意味するところは、まず1で「追加引数の[1]を参照...
-最後の0は、オペランド内の第二引数欄に0を指定せよ、という...
-8b命令や89命令では、命令コードの直後に「mod r/mバイト」...
--第一オペランドでvar[ ]変数にアクセスしたい場合、HL-17は...
-この例では第二オペランドは0なので、RAXレジスタを指定した...
--このmod r/mについての詳しいことは、たとえば https://www...
-ということでまとめると、最初の8b命令で、 RAX = var[tc[wp...
----
-次はprint命令です。phrCmpPutIcX64()関数によって、こんな...
putIcX64("%R_8b_%0m1;", &var[e[0]], 0, 0, 0); // RCX=arg...
putIcX64("%R_ff_%0m2", &var[TcSubPrint], 0, 0, 0); // 間...
-1行目で、print命令の引数をRCXに読み込みます。
-2行目の%R_ff_%m/2命令は、アセンブラでは間接CALL命令と言...
--なお、関数の呼び出し規則については http://herumi.in.coo...
----
-これですべてです。単純代入とprint命令だけならこれで十分...
** (6) 機械語を調べるためのリンク集
-https://www.wdic.org/w/SCI/ModR/M : mod R/Mについて
-https://www.wdic.org/w/SCI/REX%E3%83%97%E3%83%AA%E3%83%9...
-http://ref.x86asm.net/coder64.html : x64の機械語の一覧
** 次回に続く
-次回: [[a21_txt02_7a]]
*こめんと欄
#comment
終了行:
* 「10日くらいでできる!プログラミング言語自作入門」の続...
-(by [[K]], 2021.05.06)
** (1) はじめに
-このテキストは、「10日くらいでできる!プログラミング言語...
-このシリーズでは、言語に新規の命令を追加することは最小限...
-言語に独自の機能を加えていって言語を「進化」させていく話...
** (2) HL-17
-ということで、HL-17について説明したいと思います。これはH...
-HL-17の基本方針はこうです。
--[1]compile()関数では、内部コード出力をやめて、代わりにx...
--[2]exec()関数は不要なので削除。
--[3]機械語を出力するにあたって、putIc()のままでは全然便...
--[4]それにともない、putIc()関数は期待通りには動かなくな...
--[5]とりあえず、単純代入命令とprint命令だけputIcX64()に...
-こうすることで、HL-9aをJITコンパイラ型の言語に改造します...
-この改造をすると、言語はCPUに依存するようになります。''x...
-しかしそれでも、やるだけの価値はあります。それほど高速に...
-説明が前後していますが「JITコンパイラ」というのは「Just-...
#include <acl.c>
typedef unsigned char *String; // こう書くと String は u...
int loadText(String path, String t, int siz) → HL-4と同...
////////////////////////////////////////////////////////...
#define MAX_TC 1000 // トークンコードの最大値.
String ts[MAX_TC + 1]; // トークンの内容(文字列)を記憶.
int tl[MAX_TC + 1]; // トークンの長さ.
unsigned char tcBuf[(MAX_TC + 1) * 10]; // トークン1つ当...
int tcs = 0, tcb = 0;
AInt var[MAX_TC + 1]; // 変数.
int getTc(String s, int len) → HL-8aと同じなので省略
////////////////////////////////////////////////////////...
int isAlphabetOrNumber(unsigned char c) → HL-2と同じなの...
int lexer(String s, int tc[]) → HL-9aと同じなので省略
int tc[10000]; // トークンコード.
enum { TcSemi = 0, TcDot, TcWiCard, Tc0, Tc1, Tc2, Tc3, ...
TcEEq, TcNEq, TcLt, TcGe, TcLe, TcGt, TcPlus, TcMinu...
TcComma, TcExpr, TcExpr0, TcTmp0, TcTmp1, TcTmp2, Tc...
TcSubPrint, TcSubTime, TcSubRgb8, TcSubXorShift, TcS...
TcSubFilRct0, TcSubDrwStr0, TcSubGprDec, TcSubOpnWin...
char tcInit[] = "; . !!* 0 1 2 3 4 5 6 7 8 ( ) [ ] { } =...
"sub_print sub_time sub_aRgb8 sub_xorShift sub_aGetP...
"sub_aFilRct0 sub_aDrwStr0 sub_gprDec sub_opnWin sub...
////////////////////////////////////////////////////////...
int phrCmp_tc[32 * 100], ppc1, wpc[9], wpc1[9]; // ppc1:...
int phrCmp(int pid, String phr, int pc) → HL-7と同じなの...
////////////////////////////////////////////////////////...
#define ABI_MSW64 1 // Windows.
#define ABI_SYSV64 0 // Linuxなど.
typedef AInt *IntP; // こう書くと IntP は AInt * の代わ...
enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpC...
OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, O...
OpPrints, OpAryNew, OpAryInit, OpArySet, OpAryGet, O...
OpXorShift, OpGetPix, OpFilRct0, OpPrm, OpF16Sin, Op...
unsigned char *ic, *icq; // ic[]:内部コード、icq:ic[]へ...
void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3) ...
{
printf("putIc: error\n");
exit(1);
}
int getHex(int c) // 16進数に使える文字ならそれを0~15の...
{
if ('0' <= c && c <= '9') return c - '0';
if ('a' <= c && c <= 'f') return c - 'a' + 10;
if ('A' <= c && c <= 'F') return c - 'A' + 10;
return -1;
}
int get32(unsigned char *p)
{
return p[0] + p[1] * 256 + p[2] * 65536 + p[3] * 167...
}
void put32(unsigned char *p, AInt i)
{
p[0] = i & 0xff; // 1バイト目に、ビット0~7...
p[1] = (i >> 8) & 0xff; // 2バイト目に、ビット8~15...
p[2] = (i >> 16) & 0xff; // 3バイト目に、ビット16~2...
p[3] = (i >> 24) & 0xff; // 4バイト目に、ビット24~3...
}
unsigned char *putIcX64_rex;
void putIcX64_sub(String s, IntP a[])
{
int i, j, k;
for (i = 0; s[i] != 0; ) {
if (s[i] == ' ' || s[i] == '\t' || s[i] == '_' |...
i++;
} else if (getHex(s[i]) >= 0 && getHex(s[i + 1])...
*icq = getHex(s[i]) * 16 + getHex(s[i + 1]);
i += 2;
icq++;
} else if (s[i] == '%') {
if (s[i + 1] == 'R') {
putIcX64_rex = icq;
*icq = 0x48;
icq++;
i += 2;
continue;
}
j = s[i + 1] - '0';
if (s[i + 2] == 'm') { // mod r/m.
k = s[i + 3] - '0';
if (s[i + 3] >= 'a') { // もしかしたらこ...
k = s[i + 3] - 'a' + 10;
}
*icq = 0x85 + (k & 7) * 8;
if (k >= 8) {
*putIcX64_rex |= 4;
}
put32(icq + 1, (a[j] - var) * 8);
icq += 5;
i += 4;
continue;
}
if (s[i + 2] == 'i') { // int.
put32(icq, (AInt) a[j]);
icq += 4;
}
if (s[i + 2] == 'c') { // char.
*icq = (AInt) a[j];
icq++;
}
if (s[i + 2] == 'q') { // long long (qword).
put32(icq, (AInt) a[j]);
put32(icq + 4, ((AInt) a[j]) >> 32);
icq += 8;
}
i += 3;
} else {
printf("putIcX64: error: '%s'\n", s);
exit(1);
}
}
}
void putIcX64(String s, IntP p0, IntP p1, IntP p2, IntP ...
{
IntP a[4];
a[0] = p0;
a[1] = p1;
a[2] = p2;
a[3] = p3;
putIcX64_sub(s, a);
}
////////////////////////////////////////////////////////...
char tmp_flag[10]; // 一時変数の利用状況を管理.
int tmpAlloc() → HL-7と同じなので省略
void tmpFree(int i) → HL-7と同じなので省略
////////////////////////////////////////////////////////...
void sub_print(AInt i)
{
printf("%d\n", (int) i);
}
void initTcSub()
{
var[TcSubPrint] = (AInt) sub_print;
}
////////////////////////////////////////////////////////...
int epc, epc1; // exprのためのpcとpc1.
int exprSub(int priority); // exprSub1()が参照するので、...
int expr(int j);
int phrCmpPutIc(int pid, String phr, int pc, int *pi, in...
int exprSub1(int i, int priority, int op) → HL-7と同じな...
int exprSub(int priority) → HL-9と同じなので省略
int expr(int j) → HL-7と同じなので省略
////////////////////////////////////////////////////////...
enum { IfTrue = 0, IfFalse = 1 };
void ifgoto(int i, int not, int label) → HL-8と同じなの...
int tmpLabelNo;
int tmpLabelAlloc() → HL-8と同じなので省略
#define BInfSiz 10
int binf[BInfSiz * 100], bd, lbd; // binf:block-info, bd...
enum { BlkIf = 1, BlkFor, BlkMain }; // BlkMainをHL-9aで...
enum { IfLabel0 = 1, IfLabel1 };
enum { ForLopBgn = 1, ForCont, ForBrk, ForLbd0, ForWpc01...
int phrCmpPutIc(int pid, String phr, int pc, int *pi, in...
{
if (phrCmp(pid, phr, pc)) {
printf("phrCmpPutIc: error\n");
exit(1);
}
return 0;
}
int phrCmpPutIcX64(int pid, String phr, int pc, int *pi,...
{
int e[9], i;
if (phrCmp(pid, phr, pc)) {
e[0] = e[1] = e[2] = e[3] = e[4] = e[5] = e[6] =...
for (i = 0; i < lenExpr; i++) {
e[i] = expr(i);
}
#if (ABI_MSW64 != 0)
if (lenExpr >= 1) { putIcX64("%R_8b_%0m1;",...
if (lenExpr >= 2) { putIcX64("%R_8b_%0m2;",...
if (lenExpr >= 3) { putIcX64("%R_8b_%0m8;",...
if (lenExpr >= 4) { putIcX64("%R_8b_%0m9;",...
for (i = 4; i < lenExpr; i++) {
putIcX64("%R_8b_%0m0; %R_89_44_24_%1c;",...
}
#elif (ABI_SYSV64 != 0)
if (lenExpr >= 1) { putIcX64("%R_8b_%0m7;",...
if (lenExpr >= 2) { putIcX64("%R_8b_%0m6;",...
if (lenExpr >= 3) { putIcX64("%R_8b_%0m2;",...
if (lenExpr >= 4) { putIcX64("%R_8b_%0m1;",...
if (lenExpr >= 5) { putIcX64("%R_8b_%0m8;",...
if (lenExpr >= 6) { putIcX64("%R_8b_%0m9;",...
for (i = 6; i < lenExpr; i++) {
putIcX64("%R_8b_%0m0; %R_89_44_24_%1c;",...
}
#endif
putIcX64("%R_ff_%0m2;", &var[sub], 0, 0, 0); // ...
for (i = 0; i < lenExpr; i++) {
if (e[i] < 0) {
*err = -1;
}
tmpFree(e[i]);
}
if (pi != 0) {
*pi = tmpAlloc();
putIcX64("%R_89_%0m0;", &var[*pi], 0, 0, 0);...
}
return 1;
}
return 0;
}
////////////////////////////////////////////////////////...
int compile(String s)
{
int pc, pc1, i, j;
! unsigned char *icq1, *icp;
pc1 = lexer(s, tc);
tc[pc1++] = TcSemi; // 末尾に「;」を付け忘れることが...
tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = ...
icq = ic;
+ putIcX64("41_57; 41_56; 41_55; 41_54; 41_53; 41_52;"...
+ putIcX64("41_51; 41_50; 57; 56; 55; 54; 53; 52; 51; ...
+ putIcX64("%R_81_ec_f8_01_00_00; %R_bd_%0q;", var, 0,...
for (i = 0; i < 10; i++) {
tmp_flag[i] = 0;
}
tmpLabelNo = 0;
bd = lbd = 0;
for (pc = 0; pc < pc1; ) { // コンパイル開始.
int e0 = 0, e2 = 0;
if (phrCmp( 1, "!!*0 = !!*1;", pc)) { // 単純代入.
! putIcX86("%R_8b_%1m0; %R_89_%0m0;", &var[tc[...
} else if (phrCmp(10, "!!*0 = !!*1 + 1; if (!!*2...
(中略)
! } else if (phrCmpPutIcX86(4, "print !!**0;", pc,...
(中略)
}
if (bd > 0) {
printf("block nesting error (bd=%d, lbd=%d, pc=%...
return -1;
}
+ putIcX64("%R_81_c4_f8_01_00_00;", 0, 0, 0, 0);
+ putIcX64("58; 59; 5a; 5b; 5c; 5d; 5e; 5f; 41_58; 41_...
! putIcX64("41_5a; 41_5b; 41_5c; 41_5d; 41_5e; 41_5f; ...
icq1 = icq;
// ここにあった「goto先の設定」はいったん全部削除.
return icq1 - ic;
err:
printf("syntax error : %s %s %s %s\n", ts[tc[pc]], t...
return -1;
}
// このあたりにあったexec()関数の記述はすべて削除.
// ついでに変数winの宣言も削除.
int run(String s)
{
if (compile(s) < 0)
return 1;
+ void (*func)() = (void (*)()) ic;
! func();
return 0;
}
// 以下のmallocRWX()はWindows用の記述. Linux系のOSの場合...
#include <windows.h>
void *mallocRWX(int siz)
{
return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE...
}
////////////////////////////////////////////////////////...
void aMain()
{
unsigned char txt[10000];
int i;
+ ic = mallocRWX(1024 * 1024); // 1MB
lexer(tcInit, tc);
+ initTcSub();
if (aArgc >= 2) {
(中略)
}
-プログラムは741行になりました。このHL-17では、printと単...
>print 1
1
>print 2
2
>a=4
>print a
4
-とてもつまらなくなったのではありますが、しかしこれは全部...
** (3) HL-17に関する重要な説明
-このHL-17は、x64の64bitモード専用で書かれています。した...
-そしてこれはHL-11の説明にも書いたことですが、x64の機械語...
** (4) 今回の改造のあらまし
-今回からic[ ]に機械語を入れて実行することになるのですが...
-しかしこれができなければ、そもそもJITコンパイラなんて実...
-Windowsの場合は、上記のプログラムのように
#include <windows.h>
void *mallocRWX(int siz)
{
return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE...
}
-とすることで可能になります。
-LinuxなどのPOSIX系のOSでは、以下のようにやればできます。
#include <unistd.h>
#include <sys/mman.h>
void *mallocRWX(int siz)
{
return mmap(0, siz, PROT_READ | PROT_WRITE | PROT_EX...
}
-HL-17ではこのmallocRWX()を呼び出して、返値をicに代入して...
-ic[ ]の中に機械語を書き込んだ後は、
void (*func)() = (void (*)()) ic;
func();
-これで呼び出しています(これはrun()関数の中に書いてあり...
-2行目はただの関数呼び出しです。こうすることで、ic[ ]に書...
-ic[ ]に適切な機械語を書き込むのは、compile()関数の仕事で...
-なお、ic[ ]に機械語を書き込む際には、1バイトずつ書き込む...
** (5) 生成している機械語の説明(putIcX64()やphrCmpPutIcX...
-ここを読んでいる多くの人は、おそらくx64の機械語なんて知...
----
-まず大事なこととして、x64ではWindowsかそれ以外かで、関数...
#define ABI_MSW64 1 // Windows.
#define ABI_SYSV64 0 // Linuxなど.
-のところで選んでください(上記はWindowsの場合になってい...
-compile()では、ic[ ]の冒頭に、
putIcX64("41_57; 41_56; 41_55; 41_54; 41_53; 41_52;"...
putIcX64("41_51; 41_50; 57; 56; 55; 54; 53; 52; 51; ...
putIcX64("%R_81_ec_f8_01_00_00; %R_bd_%0q;", var, 0,...
-を書きこんでいます。また末尾には、
putIcX64("%R_81_c4_f8_01_00_00;", 0, 0, 0, 0);
putIcX64("58; 59; 5a; 5b; 5c; 5d; 5e; 5f; 41_58; 41_...
putIcX64("41_5a; 41_5b; 41_5c; 41_5d; 41_5e; 41_5f; ...
-を書き込んでいます。・・・まずはこのあたりから説明します。
-まず命令「50」~「57」は、1バイトの命令で、RAX~RDIの8つ...
-・・・こうすることで、プログラム内では16個のレジスタを自...
-命令「%R_81_ec_f8_01_00_00;」は7バイトの命令で、RSP=RSP-...
-そして「%R_bd_%0q;」はRBPレジスタに64bit定数を代入するた...
-ここでputIcX64()関数についても説明します。最初に文字列で...
----
-単純代入命令では、
putIcX64("%R_8b_%1m0; %R_89_%0m0;", &var[tc[wpc[0]]]...
-という機械語を使っています。ちなみにここはHL-9aでは
putIc(OpCpy, &var[tc[wpc[0]]], &var[tc[wpc[1]]], 0,...
-になっていました。第二引数以降は完全に同じになっています...
-命令「%R_8b」と「%R_89」はどちらもアセンブラではMOV命令...
-次の%1m0の意味するところは、まず1で「追加引数の[1]を参照...
-最後の0は、オペランド内の第二引数欄に0を指定せよ、という...
-8b命令や89命令では、命令コードの直後に「mod r/mバイト」...
--第一オペランドでvar[ ]変数にアクセスしたい場合、HL-17は...
-この例では第二オペランドは0なので、RAXレジスタを指定した...
--このmod r/mについての詳しいことは、たとえば https://www...
-ということでまとめると、最初の8b命令で、 RAX = var[tc[wp...
----
-次はprint命令です。phrCmpPutIcX64()関数によって、こんな...
putIcX64("%R_8b_%0m1;", &var[e[0]], 0, 0, 0); // RCX=arg...
putIcX64("%R_ff_%0m2", &var[TcSubPrint], 0, 0, 0); // 間...
-1行目で、print命令の引数をRCXに読み込みます。
-2行目の%R_ff_%m/2命令は、アセンブラでは間接CALL命令と言...
--なお、関数の呼び出し規則については http://herumi.in.coo...
----
-これですべてです。単純代入とprint命令だけならこれで十分...
** (6) 機械語を調べるためのリンク集
-https://www.wdic.org/w/SCI/ModR/M : mod R/Mについて
-https://www.wdic.org/w/SCI/REX%E3%83%97%E3%83%AA%E3%83%9...
-http://ref.x86asm.net/coder64.html : x64の機械語の一覧
** 次回に続く
-次回: [[a21_txt02_7a]]
*こめんと欄
#comment
ページ名: