程序計數器(Program Counter)是一個實際存在的寄存器嗎?
對程序計數器(PC)的概念不是很清楚,在我一直以來的印象里,程序計數器是一個在沒有分支和跳轉的情況下指示下一條指令地址的東西。
在很多微機原理和計算機組成原理書中說每當完成取指令操作後,PC = PC + 1,感覺這個說法不太正確,我的理解是PC=PC + 「取出的指令長度」,比如上一條指令長度為4位元組,PC=PC+4,這個理解正確嗎?又或者比如MIPS指令集的每條指令長均為4位元組,所以指令地址一定為4的倍數,所以指令地址後兩位一定為0,所以這裡的+1指代的就是+4位元組呢?還有一個問題就是因為雖說經常聽到PC這個詞,但是我卻沒有見到其實體,所以我的理解是PC實際上就是CS:IP組合的邏輯表示。PC不是一個實體,真正用來表示PC值的是CS:IP,所謂的PC自動增加是指令指針寄存器IP在自增?這個理解對嗎?
在很多微機原理和計算機組成原理書中說每當完成取指令操作後,PC = PC + 1,感覺這個說法不太正確,我的理解是 PC=PC + 「取出的指令長度」,比如上一條指令長度為 4 位元組,PC=PC+4,這個理解正確嗎?又或者比如 MIPS 指令集的每條指令長均為 4 位元組,所以指令地址一定為4的倍數,所以指令地址後兩位一定為 0,所以這裡的 +1 指代的就是 +4 位元組呢?
+1 是指增加一個——概念中的單位。為了方便教學往往說 +1,實際上是增加(一條指令的長度 ÷ 定址粒度),在 MIPS 中就是 4,因為 MIPS 一條指令長度 4 位元組,定址粒度是 1 位元組。而 x86 體系的指令長度不定,所以每次增加的量會變化。
還有一個問題就是因為雖說經常聽到 PC 這個詞,但是我卻沒有見到其實體,所以我的理解是 PC 實際上就是 CS:IP 組合的邏輯表示。PC 不是一個實體,真正用來表示 PC 值的是 CS:IP,所謂的 PC 自動增加是指令指針寄存器 IP 在自增?這個理解對嗎?
在 x86 體系里是這樣。x86 系統中自增的是 IP,用 CS:IP 組合表示正在執行的指令地址,此時 PC 只是一個概念上的說法。在 ARM 體系中 R15 就是 PC,當然 ARM 和 IA-32、x64 都支持高級內存管理,所以「PC」的內容未必是當前指令在內存中的絕對位置。
看看簡單的CPU實現會有助於理解這些概念,比如下面Verilog實現的ARMv4摘取的片段, 這裡的PC(rf,f是15的十六進位)被聲明成一個32位寄存器,會被綜合(粗略理解就是把Verilog編程源文件的過程)物理寄存器(這個寄存器不是編程概念的寄存器,一般是觸發器組成的,用來在一個時鐘周期存住二進位狀態)。
reg [31:0] rf;
rf &<= rf + 4;
程序計數器只是一個概念而已。如果你不鑽牛角尖,這個概念理解起來並不會存在什麼困難,因此它對於教學是很有意義的一個概念,這就是它存在的意義。至於說寄存器,理解程序計數器時並不需要討論寄存器這樣的概念,而且它也可以不是寄存器。比如在x86中實現PC概念用的CS:IP或者CS:EIP、CS:RIP中就不是寄存器。在學習這一類的教程的時候,還是要關注這些文字想表達的是什麼意思,而提到程序計數器這樣的詞的時候,它想表達的內容的核心絕對不是具體的中央處理器設計是如何的。
PC寄存器存在,且可以被指令直接或間接修改。
順序執行時,PC是自增的,取指後PC+=1WORD。噹噹前指令可以修改PC,比如JMP就會把參數的值寫入到PC中,這樣實際執行下一條指令時就跳到JMP指定的位置了。
如果實現一個棧,每次把一系列數據存入棧里,最後把返回地址也入棧,並JMP到新的地址,就是實現了函數調用。被調用的函數處理完成後,根據棧頂地址返回調用者,也是用到了JMP。當然有些體系結構里有專用的實現函數調用的指令。
除了JMP這類無條件跳轉外,還有有條件跳轉,比如某個標誌位為0時才會修改PC指令決定下一步指令的位置。可以用來實現if判斷。
如果有條件跳轉是判斷某個寄存器是否為0或者是否與另一個寄存器相等,也可以實現if判斷,或者for循環。while循環也是可以的,只是條件稍微複雜。
很多指令集中都有花式繁多的條件跳轉,可以用來實現各種靈活的執行流程式控制制。可以去看看就知道了。
ARM的體系結構會在指令地址的最低位做一些手腳。偶數則表示執行ARM指令,奇數表示執行Thumb指令。所以PC的值未必嚴格的表示下一條指令的地址。在指令系統層去看就是 PC = PC + 1 而已,如果你需要深入的了解,看看微體系結構的章節應該就能解除疑惑了。
有的,而且64位機器用這個寄存器來做基址讀取變數。如mov 0xdf0a(%rip),%rdx64位地址長度64位,如果不用基址,用絕對地址,那麼指令長度至少64位,8位元組。用rip寄存器來做基址訪問,可以大大縮小指令長度,一般程序代碼跟數據偏移不超過32g,那麼用32位的偏移就好了。可以減少4個位元組指令長度。同時又不用佔用額外寄存器。
wiki中已經有了明確的說明:
The program counter (PC), commonly called the instruction pointer (IP) in Intelx86 and Itaniummicroprocessors, and sometimes called the instruction address register (IAR)
鏈接地址:https://en.wikipedia.org/wiki/Program_counterPC(Program Counter)是非Intel廠家對IP( Instruction Pointer)的稱呼。
我認為,任何對科學或者工程感興趣的人,其實都經歷過題主這個階段。領略了一些系統的精妙之後,回過頭來試圖用嚴格的形式化方法來嚴格對應現實中的意義。
其實現實中,討論問題不大可能完全遵循嚴格形式化。關於這方面,《G?del Escher Bach Eternal Golden Brade》做過很有意思的討論。現實中人們的思維,即使是關於一個形式化系統的思維(更不必說計算機並不是形式化系統,只是近似形式化系統的物理系統),也有無數的捷徑跳轉,各種為了方便的近似。
題主自己已經對教科書里 PC 和實際情況的各種對應和出入有了正確的分析。所以題主的問題不是計算機問題,而是還不熟悉人類在工程中大量的不遵循嚴格形式化的習慣而已。回過頭來,如果我們討論問題的每一步都要經歷題主這樣的權衡,那麼問題的討論會無比冗長。在《GEB》里有一個嚴格形式化的證明 a+b=b+a,用了兩頁紙。
(另外,解釋語言也有 PC。Lua VM 里的 PC 是 lua_State 的 savedpc。)結論:
1.PC中存放下一次訪存的地址。
2.PC自增的確是PC+取出指令的長度。
3.PC每次自增都是固定的字長。
————————————————————————————
1.程序計數器的工作原理
大概解釋一下程序計數器吧,用一個最簡單的例子。
首先第一點:PC中存放的是下一步要訪問的內存地址。
如圖,這是一個非常非常簡單的CPU模型。
一個簡單到的只有五條指令In(put)、Add 、Sto(re) 、Out(put) 、JMP的微控制器。
每一條指令都由一系列狀態,或者說微指令組成。
對於每一條指令,都會在開始執行兩條公共的微指令01,02,
01:PC的值打入存儲器地址寄存器,PC自增。
02:存儲器的數據輸入到匯流排,再流入指令寄存器。
接下來就是每個指令各自的分支路線了,然後再重頭循環。
可以很清楚的看出,對於In指令之外的所有指令,
[PC-&>AR,PC+1] 這條微指令在整個指令執行周期中都不止執行了一次。
所以,不是說一條取指微指令執行完之後直接讓PC+指令長度,有的指令屁股後面還掛著一兩個操作數呢。在取操作數的時候,這條微指令也要執行,始終保證下一次訪存時,PC指向正確的位置。在這個過程中PC自增可能執行了不止一次,訪存幾次就執行幾次。
有時候講組織結構,為了省事,就直接說+1了。
其實我覺得說+1是有歧義的,不如換成自增....
這個自增,究竟增長了幾個位元組,就要看機器的字長了。
比如這裡的粒度是單位元組,那PC每次自增,就是後跳一位元組,也就是真正的「+1」。
2.PC寄存器是不是真的存在。程序計數器是實際存在的,別的體系我不清楚,不過x86是有的。
x86的機子里就是InstructionPointer,或者說CS:IP。因為這兩者實際的功能一模一樣嘛。不信且看。上面是一張體系結構圖。
然後到了x86中的具體實現。原來和堆棧寄存器放一起的,看,就是變了個名字嘛。它可能在某個CPU體系中真實存在,只是不存在於x86的CPU而已。考過高級程序員的應該記得當時有個casl彙編所面對的CPU體系就確實有個真實的寄存器叫pc,那麼它的原型CPU很有可能就是一款有PC寄存器的CPU,而且這款CPU的指令集恰恰是定長的。
至於指針+1在C語言裡面的定義也很明確,是加一個單位長度。如果字長為4位元組,自然它的概念是加4。所以這樣的名稱也沒明顯問題,至少在PC寄存器的那個目標CPU架構中沒問題。
可以這樣考慮問題:x86並不是世界上唯一存在的體系架構,每個體系架構中的CPU指令以及寄存器名稱都不同。所以遇到一個在自己所知體系中不存在的寄存器也就沒什麼奇怪了。你說的是 EIP 寄存器吧。
扯點別的,雖然沒有Program Counter,但是intel提供了general-purpose PMC(Performance Monitor Counter)的IA32_PERFEVTSELs含一個PC。也就是Performance Counter性能計數器里的Pin Control。
首先,確實存在PC
其次將PC作為【程序指針】而不是【程序計數器】更容易幫助理解它的作用
再次PC並不是一成不變的自加步長,程序跳轉和程序返回很多時候是通過改寫PC指針完成的(條件跳轉也是)
最後有的指令集某些指令支持條件執行,無需專門的跳轉去執行代碼分支,直接用條件指令就可以x86有段寄存器,所以pc是cs和ip的組合。其他架構就是單獨一個寄存器。另外其他架構因為指令長度固定,所以是像你說的是地址+4。你objdump反彙編你的程序,你也能看到。
PC寄存器肯定存在,有些架構的晶元還存在雙PC寄存器