Skip to content

Commit

Permalink
new update
Browse files Browse the repository at this point in the history
  • Loading branch information
WeiTheShinobi committed Jul 15, 2024
1 parent c4f9cbb commit e2affd5
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
52 changes: 51 additions & 1 deletion note/編譯器設計engineering-a-compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 344,54 @@ free list 通常是個 double linked-list,讓更新內存時更為高效。
4. 標記清除收集器:將沒標記的物件放到空閑 linked-list 中處理
5. 複製收集器:擁有新舊記憶體,將舊的記憶體內的物件複製到新的,當然只會複製還會用到的,無法被找到的物件將被釋放

## 七、代碼形式
## 七、代碼形式

編寫編譯器專注於,編譯器的實現上有多種選擇,速度快、寄存器使用少、空間少,這些差別歸因於 code shape。

例如 `switch` 可以使用 `if-else-then`、用空間換時間的陣列等等方法,編譯器考量程式的上下文選擇最好的方法。

### 分配存儲位置

兩種常見的策略:

- 記憶體到記憶體:編譯器假設所有值都在記憶體,按照需要載入到寄存器
- 寄存器到寄存器:僅在絕對必要時才將值寫進記憶體

編譯器只能控制自己的程式記憶體,實際上的邏輯地址到物理地址是由作業系統和硬體控制的

編譯器將生命週期類一致的值放在一起儲存,像是一個 procedural 內的變數就跟 ar 放在一起,跨多個 prcedural 或是靜態變數就會放在不同地方。

### 寄存器

編譯器可以保存在寄存器中的值稱為無歧義值,反之。寄存器中的歧異值必須在另一個歧異值被定義或使用之前結束。

- 無歧義值:只能一個名字訪問
- 歧異值:能被多個名字訪問,例如被多個指標指向的值

---

- 減少寄存器的需求

編譯器可以計算子樹需要的寄存器量。
編譯器可以走訪一次程式碼計算寄存器使用量,然後再輸出實際的程式碼。

---

- 值傳遞:已在寄存器則使用,否則加載到寄存器中
- 地址傳遞:已在寄存器則還需載入值,否則先載入地址後再載入值。多了一個步驟

地址是歧異值,需要注意寄存器的規則:寄存器中的歧異值必須在另一個歧異值被定義或使用之前結束。

### 左值(lvalue)、右值(rvalue)

等號左右邊的求值方式是不同的,左值求值生成 寄存器 | 記憶體地址,右值求值生成一個值,語言的實作也要決定類型。

### 布林運算和關係運算

- 數值編碼:賦值
- 位置編碼:跳轉分支

> 短路求值:很多情況下,一個子表達式能決定整個表達式的值,此時就能在此停止而不用計算完整個表達式。
### 數組

11 changes: 11 additions & 0 deletions note/計算機程序的構造和解釋SICP.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 218,14 @@ f(4) = 24
控制寄存器的賦值、立即數等等

情境繼續,現在我們需要執行兩次同樣的程式,那這種寄存器機器就需要兩個,但這不是經濟的做法,不如重複使用這個機器兩次,把第一次執行的數值保存在其他寄存器中(或者捨棄)。這會讓我們需要一個能夠控制流程的能力,需要有個能夠讓機器知道要通往程式碼第幾行的能力,這個數值也記在寄存器中。

假設有一個程式:

```python
def f(n int):
if n == 1:
return 1
return n * f(n-1)
```

注意到`f()`被調用時還需要紀錄當時的`n`,並且在計算完之後才能把`n * f(n-1)`計算完,在`f(n)`還沒完成時就要計算`f(n-1)`,需要擴充新的功能,一個 stack 的功能來紀錄每個`f()`的調用。

0 comments on commit e2affd5

Please sign in to comment.