[1314] Re:コード生成部分について
投稿者:山
2009/05/29 22:32:50
>(1)Cのように、配列の領域をスタック上に取る。
ご指摘ありがとうございます。前の書き込みからVMをいろいろ調べていましたが、
まさに(1)の方法を取ろうと思っています。このレス読んだのがちょうど今日なん
ですよ。
ちなみに、配列の添え字記述が宣言時決定でnewは無くなる方向しかないかなと
思っています。スタック上に取る以上は動的確保は無理のようで。VMコードも多少
変わってしまいました。 下記がVMコードの現在の案です。
S_OpcodeInfo D_opcode_info[] = {
{"dummy", "", 0},
{"push_int_1byte", "b", 8},
{"push_int_2byte", "s", 8},
{"push_int_4byte", "i", 8}, // 実際の値を持つ
{"push_double_0", "", 12},
{"push_double_1", "", 12},
{"push_double_8byte", "d", 12}, // 実際の値を持つ
{"push_string", "s", 40}, // 文字列の位置NOを持つ0~
{"push_null", "", 8},
/**********/
{"push_stack_int", "s", 8}, // intローカル変数をスタックに
{"push_stack_double", "s", 12}, //
{"push_stack_string", "s", 40}, //
{"pop_stack_int", "s", -8}, // スタックのintをローカル変数に
{"pop_stack_double", "s", -12}, //
{"pop_stack_string", "s", -40}, //
/**********/
{"push_static_int", "s", 8}, // int静的変数をスタックに
{"push_static_double", "s", 12}, //
{"push_static_string", "s", 40}, //
{"pop_static_int", "s", -8}, // スタックのintを静的変数に
{"pop_static_double", "s", -12}, //
{"pop_static_string", "s", -40}, //
/**********/
{"push_sysval_int", "i", 8}, // システムグローバル変数をスタックに
{"push_sysval_double", "i", 12}, //
{"push_sysval_str", "i", 40}, //
{"pop_sysval_int", "i", -8}, // スタックからシステムグローバル変数に
{"pop_sysval_double", "i", -12}, //
{"pop_sysval_str", "i", -40}, //
/**********/
{"push_array_int", "", 16}, // int配列値をスタックに
{"push_array_double", "", 20}, //
{"push_array_string", "", 16}, //
{"pop_array_int", "", -16}, // スタックからint配列に
{"pop_array_double", "", -20}, //
{"pop_array_string", "", -16}, //
/**********/
{"add_int", "", -8}, // 以下は総て算術演算子
{"add_double", "", -12},
{"add_string", "", -40},
{"sub_int", "", -8},
{"sub_double", "", -12},
{"mul_int", "", -8},
{"mul_double", "", -12},
{"div_int", "", -8},
{"div_double", "", -12},
{"mod_int", "", -8},
{"mod_double", "", -12},
{"minus_int", "", 0},
{"minus_double","", 0},
{"increment", "", 0},
{"decrement", "", 0}, // ここまで算術演算子
{"cast_int_to_double", "", 4}, // 以下はキャスト処理
{"cast_double_to_int", "", -4},
{"cast_boolean_to_string" "", 32},
{"cast_int_to_string", "", 32},
{"cast_double_to_string", "", 28}, // ここまでキャスト処理
{"eq_int", "", -8}, // 以下は総て論理演算子
{"eq_double", "", -12},
{"eq_string", "", -40},
{"gt_int", "", -8},
{"gt_double", "", -12},
{"gt_string", "", -40},
{"ge_int", "", -8},
{"ge_double", "", -12},
{"ge_string", "", -40},
{"lt_int", "", -8},
{"lt_double", "", -12},
{"lt_string", "", -40},
{"le_int", "", -8},
{"le_double", "", -12},
{"le_string", "", -40},
{"ne_int", "", -8},
{"ne_double", "", -12},
{"ne_string", "", -40},
{"logical_and", "", -8},
{"logical_or", "", -8},
{"logical_not", "", 0}, // ここまで論理演算子
{"pop", "", -1}, // スタックを1つ減らす
{"duplicate", "", 12}, // スタック内容をコピーしてスタックに
{"jump", "s", 0}, // 指定ポインターにjump
{"jump_if_true", "s", -1}, // スタックがtrueならjump
{"jump_if_false","s", -1}, // スタックがfalseならjump
/**********/
{"push_function", "sss", 0},// 関数情報をスタックに
{"invoke", "", -1}, // 関数コール
{"return", "", -1}, // 関数戻り
/**********/
{"new_array_int", "b", 16}, // 次元数 基本データ-型の配列生成
{"new_array_double", "b", 20}, // 次元数 基本データ-型の配列生成
{"new_array_object", "b", 16}, // 次元数 基本データ-型の配列生成
{"new_array_literal_int", "s", 16}, // int定数配列を変数に接続
{"new_array_literal_double","s", 20}, // double定数配列を変数に接続
{"new_array_literal_object","s", 16}, // object定数配列を変数に接続
};
(注:ちなみに1とか-1は未定です)
上の構造だとstringがやたらスタックを食っているのは、string自体の大きさ
からです。このへんが、stringを使用するのを変えてしまいそうな気分です。
ちなみに、配列は3次元まで、要素の最大は60KByteまでの制限をする予定。汎用の
言語を作るわけではないし、あくまでもローカル変数の制限だけなので、目的と
効率を優先にしています。
また、スタック構造ですが、VC++だとenumが4byte取るのでスタック対応する
フラグは消して、下記のようなS_Valueに変えて、タイプとサイズを持つことに
しました。で結局の所、関数のローカル変数は総てスタックに持つことでint用と
double当のプールはなくしました。前に話したようにVMは出来るだけシンプルに
したいので、持つデータ構造は違う形になりそうです。配列の仕様も変えてし
まったし。下記の構造体をスタックで使用します
//===================================================================
typedef struct {
ushort dimno; // 配列の次元数
ushort dimsno[DIMMAX]; // 配列の添え字数
union {
int *intDim; // int配列実態
double *doubleDim; // double配列実態
string *stringDim; // string配列実態
}u;
} SV_array;
//===================================================================
typedef struct {
ushort vtype; // データタイプ E_StackDType
ushort size; // スタック消費サイズ
union {
int int_value; // intデータ
double double_value; // doubleデータ
string *string_value // string参照
SV_array *i_array; // int配列参照
SV_array *d_array; // double配列参照
SV_array *s_array; // string配列参照
}u;
} S_Value;
以上が、今日までに想定した仕様です。まだVM自体を作っていないので、まだ
変更があるかもしれません。ちなみに、組み込み関数についてはラフ案で下記の
ようなクラスを使用しようと思っています。組み込み関数からのリターンはエラー
コードにする予定。パラメータ可変の関数も対応します。それと変数リファレンス
情報を持って一部の関数は総ての変数をリファレンスに操作します。この点は悩み
ました。文字列と配列以外は実数渡しなのですが、一部のシステム関数のみリファ
レンス操作してしまうんですよ。その一部のために構文を作るべきか特殊仕様とす
るかで、たった2つの関数のために構文を変えることは止めにしました。
目的優先に^^;
//===================================================================
typedef struct {
char *name; // 変数名
ushort gval_x; // グローバル変数横軸数
ushort gval_y; // グローバル変数縦軸数
S_Value *val_ref; // 変数参照データ
S_Value val; // 変数コピーデータ
} S_Value_info;
//===================================================================
class C_NFBase
{
//-- VMに送る情報 ----------------------
char *m_name; // 関数名
int m_prmno; // 要求するパラメータ数0-10まで20は可変関数
E_ValueType m_type[PRMMAX]; // 要求するパラメータの型
E_ValueType m_rtntype; // 関数戻り値の型
//-- VMから受け取る情報 ----------------------
int m_prninno; // 引き渡されたパラメータ数
S_Value_info m_prm[PRMMAX]; // 引き渡されたパラメータデータ
//-- 関数の戻り情報 ----------------------
S_Value m_return; // 関数からの戻り値データ