小葉老師的 KsanaVM Forth 以及 KVMCall deep call issue 的可能解法

120 views
Skip to first unread message

H.C.Chen

unread,
Sep 1, 2020, 1:18:49 AM9/1/20
to 符式協會
我找到小葉老師的 KsanaVM Forth 原版 zip 如附件, .exe 還能在 Windows 10 64bits 上跑,但是 source 已經 compile 不了,因為 inline assembly __asm 已經被禁止。我用純 C 不知好不好地解決了這個問題 @  https://github.com/hcchengithub/KsanaVM  這已經是個 64 bits cell 的版本。

小葉老師原來的 KVMCall 本來是用來「跑完」colon word 之後「回來」繼續跑以下的 words。但我找到的這版會變成用這個 KVMCall 「繼續跑之後的所有 words」,所以看到下一個 colon word 就會又 invoke 新的 KVMCall 再 invoke 新的 KVMCall 再 invoke 新的 KVMCall 。。。。 終於造成 stack overflow 為止。C 的 stack 夠大,這個情況一般不會發生,跑長點的 loop 才有機會看到這個問題。

// 有問題的 KVMCall 如下

void KVMCall(KsanaVm *vm, KVMXT startaddr)
    __int64 addr;
    vm->abortexec = 0;
    vm->ip = startaddr;
    do {
        addr = *((__int64 *)vm->ip);  
        vm->ip += POINTERSIZE;        
        if (inDictionary(vm,addr)) { 
            KVMRPush(vm,vm->ip);     
            KVMCall(vm,addr);        
        } else {
            (*(KVMXT)(addr))(vm); 
        }
    } while (!vm->abortexec) ;    
}

// 我加了一行似乎解了

void KVMCall(KsanaVm *vm, KVMXT startaddr)
    __int64 addr;
    vm->abortexec = 0;
    vm->ip = startaddr;
    do {
        addr = *((__int64 *)vm->ip);  
        vm->ip += POINTERSIZE;        
        if (inDictionary(vm,addr)) { 
            KVMRPush(vm,vm->ip);     
            KVMCall(vm,addr);        
        } else {
            (*(KVMXT)(addr))(vm); 
        }
        if (addr == KVMRet) break;  // 解決 deep call 的問題。
    } while (!vm->abortexec) ;    
}

陳厚成
汐止


Message has been deleted
Message has been deleted

H.C.Chen

unread,
Sep 14, 2020, 10:15:31 PM9/14/20
to 符式協會
問題似乎解了, 若果然則答案在這些對話裡面:

06:16 陳厚成: 小葉的 inner loop 會越鑽越深,把 CPU stack 爆掉。我的辦法 (前述的  if (addr == KVMRet) break; ) 也不週全,到現在也解不了。希望能用改用政昌兄的 inner loop 邏輯搬過來試試看。
08:45 吳政昌 : KVMCall 的作法,會在 Inner loop 中判斷 addr 是否在字典中。我想這個設計少了一個處理 colon definition 的 nest 指令。
08:57 陳厚成:  感謝指點,我得去翻看一下 NEST 指令。。。。
吳政昌 :  我不確定那個 inner loop中的遞迴會造成 CPU stack 爆掉。Forth colon definition 應該不會很深才對。另外,這個系統使用 catch throw 處理 Abort 嗎?有 error 時它如何跳出這個 recursive 呼叫?
14:30 吳政昌 這是我的 nest。感覺 KSanaVM 用副程式 call 性能會比較好。但我不能用 call,因為 Rust 沒有 catch throw。Call 了後不好離開。
14:36 吳政昌 圖片
14:39 吳政昌 這是和 nest 對應的 exit。我覺得 KasanaVm 的 CPU Stack 爆掉不一定是那個 recursive 的 call 造成。可能要確認到底這個 call 有多深。
14:48 吳政昌 KasanaVm 的這個作法會使得難以實現 multitasker。設計 Forth VM 就是這樣,有捨有得,沒有最好的設計。
14:52 陳厚成 感謝您!
2020.09.15 星期二
10:00 陳厚成 幸得 政昌兄 點撥,問題似乎解了。我從 NEST 去思索,發現這來源的 does> words 也有 inner loop 累積不退的問題。照著 NEST 的啟發,想出的辦法就是加個 exit 指令 (只舉個 flag 而已) 當作原來的 ; 指令的 decorator 把 ; 修飾成令 inner loop BREAK 出去。應用在 does> 的場合,例如 : constant create , does> r> @ exit ;   有效。
10:01 陳厚成 附註: 上面的 r> 是 yap 的得意發明,與一般 FORTH 不同。

希望這樣改不會再有別的問題:
// invoke a high level word,  the forth "inner loop"
void KVMCall(KsanaVm *vm, KVMXT startaddr)
    __int64 addr;
    vm->abortexec = 0;
    vm->ip = startaddr;
    for(;;) {
        addr = *((__int64 *)vm->ip);    // fetch the instruction from ip. Forth 是以 function entry point or colon word cfa 為 instruction.
        vm->ip += POINTERSIZE;          // advance to next CELL 還沒用就先指到下一個 word. 
        if (inDictionary(vm,addr)) {    // 這意味 dictionary 裡面的都是 KVMXT, 以 entry addrerss cfa 當 instruction 
            KVMRPush(vm,vm->ip);        // save the next xt for returning from caller , see  *1*
            KVMCall(vm,addr);           // jmp to the addr , recursive call
        } else {                        
            (*(KVMXT)(addr))(vm);       // call as a C function  ( code-word )
        }
        if (vm->abortexec == 1) break;  // abort 所有 inner loop 回到 outer loop 去          *2*
        if (addr == KVMRet) break;      // 11:59 2020/09/01 解決 deep call 的問題。 https://groups.google.com/g/figtaiwan/c/l24RIwnpjv0/m/E4PxNMcJAwAJ 
        if (vm->abortexec == 2){        // 08:04 2020/09/15 解決 does> words 的 deep call 問題。 2 只用來 BREAK 當前這一個 KVMCall 不是整個結束掉,inner loop 還沒完。
            vm->abortexec = 0;          
            break;
        }        
    }
}

// 這個是 ; 的 run time 應該就是 NEST 
void KVMRet(KsanaVm *vm)            // return to caller
{
    if (vm->rp<0) {
        vm->abortexec=1;  // quit innerloop if no more item on return stack  *2*
        return;
    }
    vm->ip = KVMRPop(vm); // recover the ip from return stack   *1*
}

// 這個是我新增的 decorator 名喚 exit 用來修飾 KVMRet 
void KVMdoExit(KsanaVm *vm)
{
    vm->abortexec = 2;  // explicitly BREAK the KVMCall 但 colon word 還沒完,讓前一級 KVMCall 繼續跑
}

// ( -- ) Compile the exit forth instruction 
void KVMExit(KsanaVm *vm)    // begin a colon compiler
{
DictPointerCompile(vm, KVMdoExit);  // must add this for does> words
}
Reply all
Reply to author
Forward
0 new messages