日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

虚拟机(基于栈还是基于寄存器)之谈

發布時間:2023/12/19 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 虚拟机(基于栈还是基于寄存器)之谈 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文章地址:http://rednaxelafx.iteye.com/blog/492667

1、解析器與解釋器?

解析器是parser,而解釋器是interpreter。兩者不是同一樣東西,不應該混用。?

前者是編譯器/解釋器的重要組成部分,也可以用在IDE之類的地方;其主要作用是進行語法分析,提取出句子的結構。廣義來說輸入一般是程序的源碼,輸出一般是語法樹(syntax tree,也叫parse tree等)抽象語法樹(abstract syntax tree,AST)。進一步剝開來,廣義的解析器里一般會有掃描器(scanner,也叫tokenizer或者lexical analyzer,詞法分析器),以及狹義的解析器(parser,也叫syntax analyzer,語法分析器)。掃描器的輸入一般是文本,經過詞法分析,輸出是將文本切割為單詞的流。狹義的解析器輸入是單詞的流,經過語法分析,輸出是語法樹或者精簡過的AST。?
(在一些編譯器/解釋器中,解析也可能與后續的語義分析、代碼生成或解釋執行等步驟融合在一起,不一定真的會構造出完整的語法樹。但概念上說解析器就是用來抽取句子結構用的,而語法樹就是表示句子結構的方式。關于邊解析邊解釋執行的例子,可以看看這帖的計算器。)?
舉例:將i = a + b * c作為源代碼輸入到解析器里,則廣義上的解析器的工作流程如下圖:?
?
其中詞法分析由掃描器完成,語法分析由狹義的解析器完成。?
(嗯,說來其實“解析器”這詞還是按狹義用法比較準確。把掃描器和解析器合起來叫解析器總覺得怪怪的,但不少人這么用,這里就將就下吧 =_=?
不過近來“scannerless parsing”也挺流行的:不區分詞法分析與語法分析,沒有單獨的掃描器,直接用解析器從源碼生成語法樹。這倒整個就是解析器了,沒狹不狹義的問題)?

后者則是實現程序執行的一種實現方式,與編譯器相對。它直接實現程序源碼的語義,輸入是程序源碼,輸出則是執行源碼得到的計算結果;編譯器的輸入與解釋器相同,而輸出是用別的語言實現了輸入源碼的語義的程序。通常編譯器的輸入語言比輸出語言高級,但不一定;也有輸入輸出是同種語言的情況,此時編譯器很可能主要用于優化代碼。?
舉例:把同樣的源碼分別輸入到編譯器與解釋器中,得到的輸出不同:?
?
值得留意的是,編譯器生成出來的代碼執行后的結果應該跟解釋器輸出的結果一樣——它們都應該實現源碼所指定的語義。?

在很多地方都看到解析器與解釋器兩個不同的東西被混為一談,感到十分無奈。?
最近某本引起很多關注的書便在開篇給讀者們當頭一棒,介紹了“JavaScript解析機制”。“編譯”和“預處理”也順帶混為一談了,還有“預編譯” 0_0?
我一直以為“預編譯”應該是ahead-of-time compilation的翻譯,是與“即時編譯”(just-in-time compilation,JIT)相對的概念。另外就是PCH(precompile header)這種用法,把以前的編譯結果緩存下來稱為“預編譯”。把AOT、PCH跟“預處理”(preprocess)混為一談真是詭異。算了,我還是不要淌這渾水的好……打住。?


2、“解釋器”到底是什么?“解釋型語言”呢??

很多資料會說,Python、Ruby、JavaScript都是“解釋型語言”,是通過解釋器來實現的。這么說其實很容易引起誤解:語言一般只會定義其抽象語義,而不會強制性要求采用某種實現方式。?
例如說C一般被認為是“編譯型語言”,但C的解釋器也是存在的,例如Ch。同樣,C++也有解釋器版本的實現,例如Cint。?
一般被稱為“解釋型語言”的是主流實現為解釋器的語言,但并不是說它就無法編譯。例如說經常被認為是“解釋型語言”的Scheme就有好幾種編譯器實現,其中率先支持R6RS規范的大部分內容的是Ikarus,支持在x86上編譯Scheme;它最終不是生成某種虛擬機的字節碼,而是直接生成x86機器碼。?

解釋器就是個黑箱,輸入是源碼,輸出就是輸入程序的執行結果,對用戶來說中間沒有獨立的“編譯”步驟。這非常抽象,內部是怎么實現的都沒關系,只要能實現語義就行。你可以寫一個C語言的解釋器,里面只是先用普通的C編譯器把源碼編譯為in-memory image,然后直接調用那個image去得到運行結果;用戶拿過去,發現直接輸入源碼可以得到源程序對應的運行結果就滿足需求了,無需在意解釋器這個“黑箱子”里到底是什么。?
實際上很多解釋器內部是以“編譯器+虛擬機”的方式來實現的,先通過編譯器將源碼轉換為AST或者字節碼,然后由虛擬機去完成實際的執行。所謂“解釋型語言”并不是不用編譯,而只是不需要用戶顯式去使用編譯器得到可執行代碼而已。?

那么虛擬機(virtual machine,VM)又是什么?在許多不同的場合,VM有著不同的意義。如果上下文是Java、Python這類語言,那么一般指的是高級語言虛擬機(high-level language virtual machine,HLL VM),其意義是實現高級語言的語義。VM既然被稱為“機器”,一般認為輸入是滿足某種指令集架構(instruction set architecture,ISA)的指令序列,中間轉換為目標ISA的指令序列并加以執行,輸出為程序的執行結果的,就是VM。源與目標ISA可以是同一種,這是所謂same-ISA VM。?
前面提到解釋器中的編譯器的輸出可能是AST,也可能是字節碼之類的指令序列;一般會把執行后者的程序稱為VM,而執行前者的還是籠統稱為解釋器或者樹遍歷式解釋器(tree-walking interpreter)。這只是種習慣而已,并沒有多少確鑿的依據。只不過線性(相對于樹形)的指令序列看起來更像一般真正機器會執行的指令序列而已。?
其實我覺得把執行AST的也叫VM也沒啥大問題。如果認同這個觀點,那么把DLR看作一種VM也就可以接受了——它的“指令集”就是樹形的Expression Tree。?

VM并不是神奇的就能執行代碼了,它也得采用某種方式去實現輸入程序的語義,并且同樣有幾種選擇:“編譯”,例如微軟的.NET中的CLR;“解釋”,例如CPython、CRuby 1.9,許多老的JavaScript引擎等;也有介于兩者之間的混合式,例如Sun的JVM,HotSpot。如果采用編譯方式,VM會把輸入的指令先轉換為某種能被底下的系統直接執行的形式(一般就是native code),然后再執行之;如果采用解釋方式,則VM會把輸入的指令逐條直接執行。?
換個角度說,我覺得采用編譯和解釋方式實現虛擬機最大的區別就在于是否存下目標代碼:編譯的話會把輸入的源程序以某種單位(例如基本塊/函數/方法/trace等)翻譯生成為目標代碼,并存下來(無論是存在內存中還是磁盤上,無所謂),后續執行可以復用之;解釋的話則是把源程序中的指令逐條解釋,不生成也不存下目標代碼,后續執行沒有多少可復用的信息。有些稍微先進一點的解釋器可能會優化輸入的源程序,把滿足某些模式的指令序列合并為“超級指令”;這么做就是朝著編譯的方向推進。后面講到解釋器的演化時再討論超級指令吧。?


如果一種語言的主流實現是解釋器,其內部是編譯器+虛擬機,而虛擬機又是采用解釋方式實現的,或者內部實現是編譯器+樹遍歷解釋器,那它就是名副其實的“解釋型語言”。如果內部用的虛擬機是用編譯方式實現的,其實跟普遍印象中的“解釋器”還是挺不同的……?

可以舉這樣一個例子:ActionScript 3,一般都被認為是“解釋型語言”對吧?但這種觀點到底是把FlashPlayer整體看成一個解釋器,因而AS3是“解釋型語言”呢?還是認為FlashPlayer中的虛擬機采用解釋執行方案,因而AS3是“解釋型語言”呢??
其實Flash或Flex等從AS3生成出來的SWF文件里就包含有AS字節碼(ActionScript Byte Code,ABC)。等到FlashPlayer去執行SWF文件,或者說等到AVM2(ActionScript Virtual Machine 2)去執行ABC時,又有解釋器和JIT編譯器兩種實現。這種需要讓用戶顯式進行編譯步驟的語言,到底是不是“解釋型語言”呢?呵呵。所以我一直覺得“編譯型語言”跟“解釋型語言”的說法太模糊,不太好。?
有興趣想體驗一下從命令行編譯“裸”的AS3文件得到ABC文件,再從命令行調用AVM2去執行ABC文件的同學,可以從這帖下載我之前從源碼編譯出來的AVM2,自己玩玩看。例如說要編譯一個名為test.as的文件,用下列命令:?

Command prompt代碼??
  • java?-jar?asc.jar?-import?builtin.abc?-import?toplevel.abc?test.as??

  • 就是用ASC將test.as編譯,得到test.abc。接著用:?
    Command prompt代碼??
  • avmplus?test.abc??

  • 就是用AVM2去執行程序了。很生動的體現出“編譯器+虛擬機”的實現方式。?
    這個“裸”的AVM2沒有帶Flash或Flex的類庫,能用的函數和類都有限。不過AS3語言實現是完整的。可以用print()函數來向標準輸出流寫東西。?
    Well……其實寫Java程序不也是這樣么?現在也確實還有很多人把Java稱為“解釋型語言”,完全無視Java代碼通常是經過顯式編譯步驟才得到.class文件,而有些JVM是采用純JIT編譯方式實現的,內部沒解釋器,例如 JRockit Maxine VM Jikes RVM 。我愈發感到“解釋型語言”是個應該避開的用語 =_=?

    關于虛擬機,有本很好的書絕對值得一讀, 《虛擬機——系統與進程的通用平臺》(Virtual Machines: Versatile Platforms for Systems and Processes) 。國內有影印版也有 中文版 ,我是讀了影印版,不太清楚中文版的翻譯質量如何。據說翻譯得還行,我無法印證。?


    3、基于棧與基于寄存器的指令集架構 ?

    用C的語法來寫這么一個語句:?
    C代碼??
  • a?=?b?+?c;??

  • 如果把它變成這種形式:?
    add a, b, c?
    那看起來就更像機器指令了,對吧?這種就是所謂“三地址指令”(3-address instruction),一般形式為:?
    op dest, src1, src2?
    許多操作都是二元運算+賦值。三地址指令正好可以指定兩個源和一個目標,能非常靈活的支持二元操作與賦值的組合。ARM處理器的主要指令集就是三地址形式的。?

    C里要是這樣寫的話:?
    C代碼??
  • a?+=?b;??

  • 變成:?
    add a, b?
    這就是所謂“二地址指令”,一般形式為:?
    op dest, src?
    它要支持二元操作,就只能把其中一個源同時也作為目標。上面的add a, b在執行過后,就會破壞a原有的值,而b的值保持不變。x86系列的處理器就是二地址形式的。?

    上面提到的三地址與二地址形式的指令集,一般就是通過“基于寄存器的架構”來實現的。例如典型的RISC架構會要求除load和store以外,其它用于運算的指令的源與目標都要是寄存器。?

    顯然,指令集可以是任意“n地址”的,n屬于自然數。那么一地址形式的指令集是怎樣的呢??
    想像一下這樣一組指令序列:?
    add 5?
    sub 3?
    這只指定了操作的源,那目標是什么?一般來說,這種運算的目標是被稱為“累加器”(accumulator)的專用寄存器,所有運算都靠更新累加器的狀態來完成。那么上面兩條指令用C來寫就類似:?
    C代碼??
  • acc?+=?5;??
  • acc?-=?3;??

  • 只不過acc是“隱藏”的目標。基于累加器的架構近來比較少見了,在很老的機器上繁榮過一段時間。?

    那“n地址”的n如果是0的話呢??
    看這樣一段Java字節碼:?
    Java bytecode代碼??
  • iconst_1??
  • iconst_2??
  • iadd??
  • istore_0??

  • 注意那個iadd(表示整型加法)指令并沒有任何參數。連源都無法指定了,零地址指令有什么用???
    零地址意味著源與目標都是隱含參數,其實現依賴于一種常見的數據結構——沒錯,就是棧。上面的iconst_1、iconst_2兩條指令,分別向一個叫做“求值棧”(evaluation stack,也叫做operand stack“操作數棧”或者expression stack“表達式棧”)的地方壓入整型常量1、2。iadd指令則從求值棧頂彈出2個值,將值相加,然后把結果壓回到棧頂。istore_0指令從求值棧頂彈出一個值,并將值保存到局部變量區的第一個位置(slot 0)。?
    零地址形式的指令集一般就是通過“基于棧的架構”來實現的。請一定要注意,這個棧是指“求值棧”,而不是與系統調用棧(system call stack,或者就叫system stack)。千萬別弄混了。有些虛擬機把求值棧實現在系統調用棧上,但兩者概念上不是一個東西。?

    由于指令的源與目標都是隱含的,零地址指令的“密度”可以非常高——可以用更少空間放下更多條指令。因此在空間緊缺的環境中,零地址指令是種可取的設計。但零地址指令要完成一件事情,一般會比二地址或者三地址指令許多更多條指令。上面Java字節碼做的加法,如果用x86指令兩條就能完成了:?
    X86 asm代碼??
  • mov??eax,?1??
  • add??eax,?2??

  • (好吧我犯規了,istore_0對應的保存我沒寫。但假如局部變量比較少的話也不必把EAX的值保存(“溢出”,register spilling)到調用棧上,就這樣吧 =_=?
    其實就算把結果保存到棧上也就是多一條指令而已……)?

    一些比較老的解釋器,例如 CRuby 在1.9引入 YARV 作為新的VM之前的解釋器,還有SquirrleFish之前的老JavaScriptCore以及它的前身KJS,它們內部是樹遍歷式解釋器;解釋器遞歸遍歷樹,樹的每個節點的操作依賴于解釋其各個子節點返回的值。這種解釋器里沒有所謂的求值棧,也沒有所謂的虛擬寄存器,所以不適合以“基于棧”或“基于寄存器”去描述。?

    而像V8那樣直接編譯JavaScript生成機器碼,而不通過中間的字節碼的中間表示的JavaScript引擎,它內部有虛擬寄存器的概念,但那只是普通native編譯器的正常組成部分。我覺得也不應該用“基于棧”或“基于寄存器”去描述它。?
    V8在內部也用了“求值棧”(在V8里具體叫“表達式棧”)的概念來簡化生成代碼的過程,在編譯過程中進行“抽象解釋”,使用所謂“虛擬棧幀”來記錄局部變量與求值棧的狀態;但在真正生成代碼的時候會做窺孔優化,消除冗余的push/pop,將許多對求值棧的操作轉變為對寄存器的操作,以此提高代碼質量。于是最終生成出來的代碼看起來就不像是基于棧的代碼了。?

    關于JavaScript引擎的實現方式,下文會再提到。?


    4、基于棧與基于寄存器架構的VM,用哪個好? ?

    如果是要模擬現有的處理器,那沒什么可選的,原本處理器采用了什么架構就只能以它為源。但HLL VM的架構通常可以自由構造,有很大的選擇余地。為什么許多主流HLL VM,諸如JVM、CLI、CPython、CRuby 1.9等,都采用了基于棧的架構呢?我覺得這有三個主要原因:?

    ·實現簡單?
    由于指令中不必顯式指定源與目標,VM可以設計得很簡單,不必考慮為臨時變量分配空間的問題,求值過程中的臨時數據存儲都讓求值棧包辦就行。?
    更新:回帖中cscript指出了這句不太準確,應該是針對基于棧架構的指令集生成代碼的編譯器更容易實現,而不是VM更容易實現。 ?

    ·該VM是為某類資源非常匱乏的硬件而設計的?
    這類硬件的存儲器可能很小,每一字節的資源都要節省。零地址指令比其它形式的指令更緊湊,所以是個自然的選擇。?

    ·考慮到可移植性?
    處理器的特性各個不同:典型的CISC處理器的通用寄存器數量很少,例如32位的x86就只有8個32位通用寄存器(如果不算EBP和ESP那就是6個,現在一般都算上);典型的RISC處理器的各種寄存器數量多一些,例如ARM有16個32位通用寄存器,Sun的SPARC在一個寄存器窗口里則有24個通用寄存器(8 in,8 local,8 out)。?
    假如一個VM采用基于寄存器的架構(它接受的指令集大概就是二地址或者三地址形式的),為了高效執行,一般會希望能把源架構中的寄存器映射到實際機器上寄存器上。但是VM里有些很重要的輔助數據會經常被訪問,例如一些VM會保存源指令序列的程序計數器(program counter,PC),為了效率,這些數據也得放在實際機器的寄存器里。如果源架構中寄存器的數量跟實際機器的一樣,或者前者比后者更多,那源架構的寄存器就沒辦法都映射到實際機器的寄存器上;這樣VM實現起來比較麻煩,與能夠全部映射相比效率也會大打折扣。像Dalvik VM的解釋器實現,就是把虛擬寄存器全部映射到棧幀(內存)里的,這跟把局部變量區與操作數棧都映射到內存里的JVM解釋器實現相比實際區別不太大。?


    如果一個VM采用基于棧的架構,則無論在怎樣的實際機器上,都很好實現——它的源架構里沒有任何通用寄存器,所以實現VM時可以比較自由的分配實際機器的寄存器。于是這樣的VM可移植性就比較高。作為優化,基于棧的VM可以用編譯方式實現,“求值棧”實際上也可以由編譯器映射到寄存器上,減輕數據移動的開銷。?

    回到主題,基于棧與基于寄存器的架構,誰更快?看看現在的實際處理器,大多都是基于寄存器的架構,從側面反映出它比基于棧的架構更優秀。?
    而對于VM來說,源架構的求值棧或者寄存器都可能是用實際機器的內存來模擬的,所以性能特性與實際硬件又有點不同。一般認為基于寄存器的架構對VM來說也是更快的,原因是:雖然零地址指令更緊湊,但完成操作需要更多的load/store指令,也意味著更多的指令分派(instruction dispatch)次數與內存訪問次數;訪問內存是執行速度的一個重要瓶頸,二地址或三地址指令雖然每條指令占的空間較多,但總體來說可以用更少的指令完成操作,指令分派與內存訪問次數都較少。?
    這方面有篇被引用得很多的論文講得比較清楚, Virtual Machine Showdown: Stack Versus Registers ,是在VEE 2005發表的。VEE是Virtual Execution Environment的縮寫,是ACM下SIGPLAN組織的一個會議,專門研討虛擬機的設計與實現的。可以去找找這個會議往年的論文,很多都值得讀。?


    5、樹遍歷解釋器圖解 ?

    在演示基于棧與基于寄存器的VM的例子前,先回頭看看更原始的解釋器形式。?
    前面提到解析器的時候用了i = a + b * c的例子,現在讓我們來看看由解析器生成的AST要是交給一個樹遍歷解釋器,會如何被解釋執行呢??

    用文字說不夠形象,還是看圖吧:?
    ?
    這是對AST的后序遍歷:假設有一個eval(Node n)函數,用于解釋AST上的每個節點;在解釋一個節點時如果依賴于子樹的操作,則對子節點遞歸調用eval(Node n),從這些遞歸調用的返回值獲取需要的值(或副作用)——也就是說子節點都eval好了之后,父節點才能進行自己的eval——典型的后序遍歷。?
    (話說,上圖中節點左下角有藍色標記的說明那是節點的“內在屬性”。從 屬性語法 的角度看,如果一個節點的某個屬性的值只依賴于自身或子節點,則該屬性被稱為“綜合屬性”(synthesized attribute);如果一個節點的某個屬性只依賴于自身、父節點和兄弟節點,則該屬性被稱為“繼承屬性”(inherited attribute)。上圖中節點右下角的紅色標記都只依賴子節點來計算,顯然是綜合屬性。)?

    SquirrelFish之前的JavaScriptCore、CRuby 1.9之前的CRuby就都是采用這種方式來解釋執行的。?

    可能需要說明的:?
    ·左值與右值?
    在源代碼i = a + b * c中,賦值符號左側的i是一個標識符,表示一個變量,取的是變量的“左值”(也就是與變量i綁定的存儲單元);右側的a、b、c雖然也是變量,但取的是它們的右值(也就是與變量綁定的存儲單元內的值)。在許多編程語言中,左值與右值在語法上沒有區別,它們實質的差異容易被忽視。一般來說左值可以作為右值使用,反之則不一定。例如數字1,它自身有值就是1,可以作為右值使用;但它沒有與可賦值的存儲單元相綁定,所以無法作為左值使用。?
    左值不一定只是簡單的變量,還可以是數組元素或者結構體的域之類,可能由復雜的表達式所描述。因此左值也是需要計算的。?

    ·優先級、結合性與求值順序?
    這三個是不同的概念,卻經常被混淆。通過AST來看就很容易理解:(假設源碼是從左到右輸入的)?
    所謂 優先級 ,就是不同操作相鄰出現時,AST節點與根的距離的關系。優先級高的操作會更遠離根,優先級低的操作會更接近根。為什么?因為整棵AST是以后序遍歷求值的,顯然節點離根越遠就越早被求值。?
    所謂 結合性 ,就是當同類操作相鄰出現時,操作的先后順序同AST節點與根的距離的關系。如果是左結合,則先出現的操作對應的AST節點比后出現的操作的節點離根更遠;換句話說,先出現的節點會是后出現節點的子節點。
    所謂 求值順序 ,就是在遍歷子節點時的順序。對二元運算對應的節點來說,先遍歷左子節點再遍歷右子節點就是從左到右的求值順序,反之則是從右到左的求值順序。?
    這三個概念與運算的聯系都很緊密,但實際描述的是不同的關系。前兩者是解析器根據語法生成AST時就已經決定好的,后者則是解釋執行或者生成代碼而去遍歷AST時決定的。?
    在沒有副作用的環境中,給定優先級與結合性,則無論求值順序是怎樣的都能得到同樣的結果;而在有副作用的環境中,求值順序會影響結果。?

    賦值運算雖然是右結合的,但仍然可以用從左到右的求值順序;事實上Java、C#等許多語言都在規范里寫明表達式的求值順序是從左到右的。上面的例子中就先遍歷的=的左側,求得i的左值;再遍歷=的右側,得到表達式的值23;最后執行=自身,完成對i的賦值。?
    所以如果你要問:賦值在類似C的語言里明明是右結合的運算,為什么你先遍歷左子樹再遍歷右子樹?上面的說明應該能讓你發現你把結合性與求值順序混為一談了。?

    看看Java從左到右求值順序的例子:?
    Java代碼??
  • public?class?EvalOrderDemo?{??
  • ????public?static?void?main(String[]?args)?{??
  • ????????int[]?arr?=?new?int[1];??
  • ????????int?a?=?1;??
  • ????????int?b?=?2;??
  • ????????arr[0]?=?a?+?b;??
  • ????}??
  • }??

  • 由javac編譯,得到arr[0] = a + b對應的字節碼是:?
    Java bytecode代碼??
  • //?左子樹:數組下標??
  • //?a[0]??
  • aload_1??
  • iconst_0??
  • ??
  • //?右子樹:加法??
  • //?a??
  • iload_2??
  • //?b??
  • iload_3??
  • //?+??
  • iadd??
  • ??
  • //?根節點:賦值??
  • iastore??



  • 6、從樹遍歷解釋器進化為基于棧的字節碼解釋器的前端 ?

    如果你看到樹形結構與后序遍歷,并且知道后綴記法(或者逆波蘭記法, reverse Polish notation )的話,那敏銳的你或許已經察覺了:要解釋執行AST,可以先通過后序遍歷AST生成對應的后綴記法的操作序列,然后再解釋執行該操作序列。這樣就把樹形結構壓扁,成為了線性結構。?
    樹遍歷解釋器對AST的求值其實隱式依賴于調用棧:eval(Node n)的遞歸調用關系是靠調用棧來維護的。后綴表達式的求值則通常顯式依賴于一個棧,在遇到操作數時將其壓入棧中,遇到運算時將合適數量的值從棧頂彈出進行運算,再將結果壓回到棧上。這種描述看起來眼熟么?沒錯,后綴記法的求值中的核心數據結構就是前文提到過的“求值棧”(或者叫操作數棧,現在應該更好理解了)。后綴記法也就與基于棧的架構聯系了起來:后者可以很方便的執行前者。同理,零地址指令也與樹形結構聯系了起來:可以通過一個棧方便的把零地址指令序列再轉換回到樹的形式。?

    Java字節碼與Java源碼聯系緊密,前者可以看成后者的后綴記法。如果想在JVM上開發一種語義能直接映射到Java上的語言,那么編譯器很好寫:秘訣就是后序遍歷AST。?
    那么讓我們再來看看,同樣是i = a + b * c這段源碼對應的AST,生成Java字節碼的例子:?
    ?
    (假設a、b、c、i分別被分配到局部變量區的slot 0到slot 3)?
    能看出Java字節碼與源碼間的對應關系了么??
    一個Java編譯器的輸入是Java源代碼,輸出是含有Java字節碼的.class文件。它里面主要包含掃描器與解析器,語義分析器(包括類型檢查器/類型推導器等),代碼生成器等幾大部分。上圖所展示的就是代碼生成器的工作。對Java編譯器來說,代碼生成就到字節碼的層次就結束了;而對native編譯器來說,這里剛到生成中間表示的部分,接下去是優化與最終的代碼生成。?

    如果你對 Python CRuby 1.9 之類有所了解,會發現它們的字節碼跟Java字節碼在“基于棧”的這一特征上非常相似。其實它們都是由“編譯器+VM”構成的,概念上就像是Java編譯器與JVM融為一體一般。?
    從這點看,Java與Python和Ruby可以說是一條船上的。雖說內部具體實現的顯著差異使得先進的JVM比簡單的JVM快很多,而JVM又普遍比Python和Ruby快很多。?

    當解釋器中用于解釋執行的中間代碼是樹形時,其中能被稱為“編譯器”的部分基本上就是解析器;中間代碼是線性形式(如字節碼)時,其中能被稱為編譯器的部分就包括上述的代碼生成器部分,更接近于所謂“完整的編譯器”;如果虛擬機是基于寄存器架構的,那么編譯器里至少還得有虛擬寄存器分配器,又更接近“完整的編譯器”了。?


    7、基于棧與基于寄存器架構的VM的一組圖解 ?

    要是拿兩個分別實現了基于棧與基于寄存器架構、但沒有直接聯系的VM來對比,效果或許不會太好。現在恰巧有兩者有緊密聯系的例子——JVM與Dalvik VM。JVM的字節碼主要是零地址形式的,概念上說JVM是基于棧的架構。Google Android平臺上的應用程序的主要開發語言是Java,通過其中的 Dalvik VM 來運行Java程序。為了能正確實現語義,Dalvik VM的許多設計都考慮到與JVM的兼容性;但它卻采用了基于寄存器的架構,其字節碼主要是二地址/三地址混合形式的,乍一看可能讓人納悶。考慮到Android有明確的目標:面向移動設備,特別是最初要對ARM提供良好的支持。ARM9有16個32位通用寄存器,Dalvik VM的架構也常用16個虛擬寄存器(一樣多……沒辦法把虛擬寄存器全部直接映射到硬件寄存器上了);這樣Dalvik VM就不用太顧慮可移植性的問題,優先考慮在ARM9上以高效的方式實現,發揮基于寄存器架構的優勢。?
    Dalvik VM的主要設計者 Dan Bornstein 在Google I/O 2008上做過一個 關于Dalvik內部實現 的演講;同一演講也在Google Developer Day 2008 China和Japan等會議上重復過。這個演講中Dan特別提到了Dalvik VM與JVM在字節碼設計上的區別,指出Dalvik VM的字節碼可以用更少指令條數、更少內存訪問次數來完成操作。(看不到YouTube的請自行想辦法)?

    眼見為實。要自己動手感受一下該例子,請先確保已經正確安裝JDK 6,并從 官網 獲取Android SDK 1.6R1。連不上官網的也請自己想辦法。?

    創建Demo.java文件,內容為:?
    Java代碼??
  • public?class?Demo?{??
  • ????public?static?void?foo()?{??
  • ????????int?a?=?1;??
  • ????????int?b?=?2;??
  • ????????int?c?=?(a?+?b)?*?5;??
  • ????}??
  • }??

  • 通過javac編譯,得到Demo.class。通過javap可以看到foo()方法的字節碼是:?
    Java bytecode代碼??
  • 0:??iconst_1??
  • 1:??istore_0??
  • 2:??iconst_2??
  • 3:??istore_1??
  • 4:??iload_0??
  • 5:??iload_1??
  • 6:??iadd??
  • 7:??iconst_5??
  • 8:??imul??
  • 9:??istore_2??
  • 10:?return??


  • 接著用Android SDK里platforms\android-1.6\tools目錄中的dx工具將Demo.class轉換為dex格式。轉換時可以直接以文本形式dump出dex文件的內容。使用下面的命令:?
    Command prompt代碼??
  • dx?--dex?--verbose?--dump-to=Demo.dex.txt?--dump-method=Demo.foo?--verbose-dump?Demo.class??

  • 可以看到foo()方法的字節碼是:?
    Dalvik bytecode代碼??
  • 0000:?const/4???????v0,?#int?1?//?#1??
  • 0001:?const/4???????v1,?#int?2?//?#2??
  • 0002:?add-int/2addr?v0,?v1??
  • 0003:?mul-int/lit8??v0,?v0,?#int?5?//?#05??
  • 0005:?return-void??

  • (原本的輸出里還有些code-address、local-snapshot等,那些不是字節碼的部分,可以忽略。)?

    讓我們看看兩個版本在概念上是如何工作的。?
    JVM:?
    ?
    (圖中數字均以十六進制表示。其中字節碼的一列表示的是字節碼指令的實際數值,后面跟著的助記符則是其對應的文字形式。標記為紅色的值是相對上一條指令的執行狀態有所更新的值。下同)?
    說明:Java字節碼以1字節為單元。上面代碼中有11條指令,每條都只占1單元,共11單元==11字節。?
    程序計數器是用于記錄程序當前執行的位置用的。對Java程序來說,每個線程都有自己的PC。PC以字節為單位記錄當前運行位置里方法開頭的偏移量。?
    每個線程都有一個Java棧,用于記錄Java方法調用的“活動記錄”(activation record)。Java棧以幀(frame)為單位線程的運行狀態,每調用一個方法就會分配一個新的棧幀壓入Java棧上,每從一個方法返回則彈出并撤銷相應的棧幀。?
    每個棧幀包括局部變量區、求值棧(JVM規范中將其稱為“操作數棧”)和其它一些信息。局部變量區用于存儲方法的參數與局部變量,其中參數按源碼中從左到右順序保存在局部變量區開頭的幾個slot。求值棧用于保存求值的中間結果和調用別的方法的參數等。兩者都以字長(32位的字)為單位,每個slot可以保存byte、short、char、int、float、reference和returnAddress等長度小于或等于32位的類型的數據;相鄰兩項可用于保存long和double類型的數據。每個方法所需要的局部變量區與求值棧大小都能夠在編譯時確定,并且記錄在.class文件里。?
    在上面的例子中,Demo.foo()方法所需要的局部變量區大小為3個slot,需要的求值棧大小為2個slot。Java源碼的a、b、c分別被分配到局部變量區的slot 0、slot 1和slot 2。可以觀察到Java字節碼是如何指示JVM將數據壓入或彈出棧,以及數據是如何在棧與局部變量區之前流動的;可以看到數據移動的次數特別多。動畫里可能不太明顯,iadd和imul指令都是要從求值棧彈出兩個值運算,再把結果壓回到棧上的;光這樣一條指令就有3次概念上的數據移動了。?

    對了,想提醒一下:Java的局部變量區并不需要把某個局部變量固定分配在某個slot里;不僅如此,在一個方法內某個slot甚至可能保存不同類型的數據。如何分配slot是編譯器的自由。從類型安全的角度看,只要對某個slot的一次load的類型與最近一次對它的store的類型匹配,JVM的字節碼校驗器就不會抱怨。以后再找時間寫寫這方面。?

    Dalvik VM:?
    ?
    說明:Dalvik字節碼以16位為單元(或許叫“雙字節碼”更準確 =_=|||)。上面代碼中有5條指令,其中mul-int/lit8指令占2單元,其余每條都只占1單元,共6單元==12字節。?
    與JVM相似,在Dalvik VM中每個線程都有自己的PC和調用棧,方法調用的活動記錄以幀為單位保存在調用棧上。PC記錄的是以16位為單位的偏移量而不是以字節為單位的。?
    與JVM不同的是,Dalvik VM的棧幀中沒有局部變量區與求值棧,取而代之的是一組虛擬寄存器。每個方法被調用時都會得到自己的一組虛擬寄存器。常用v0-v15這16個,也有少數指令可以訪問v0-v255范圍內的256個虛擬寄存器。與JVM相同的是,每個方法所需要的虛擬寄存器個數都能夠在編譯時確定,并且記錄在.dex文件里;每個寄存器都是字長(32位),相鄰的一對寄存器可用于保存64位數據。方法的參數按源碼中從左到右的順序保存在末尾的幾個虛擬寄存器里。?
    與JVM版相比,可以發現Dalvik版程序的指令數明顯減少了,數據移動次數也明顯減少了,用于保存臨時結果的存儲單元也減少了。?

    你可能會抱怨:上面兩個版本的代碼明明不對應:JVM版到return前完好持有a、b、c三個變量的值;而Dalvik版到return-void前只持有b與c的值(分別位于v0與v1),a的值被刷掉了。?
    但注意到a與b的特征:它們都只在聲明時接受過一次賦值,賦值的源是常量。這樣就可以對它們應用 常量傳播 ,將?
    Java代碼??
  • int?c?=?(a?+?b)?*?5;??

  • 替換為?
    Java代碼??
  • int?c?=?(1?+?2)?*?5;??

  • 然后可以再對c的初始化表達式應用常量折疊,進一步替換為:?
    Java代碼??
  • int?c?=?15;??

  • 把變量的每次狀態更新(包括初始賦值在內)稱為變量的一次“定義”(definition),把每次訪問變量(從變量讀取值)稱為變量的一次“使用”(use),則可以把代碼整理為“使用-定義鏈”(簡稱UD鏈, use-define chain )。顯然,一個變量的某次定義要被使用過才有意義。上面的例子經過常量傳播與折疊后,我們可以分析得知變量a、b、c都只被定義而沒有被使用。于是它們的定義就成為了無用代碼(dead code),可以安全的被消除。?
    上面一段的分析用一句話描述就是:由于foo()里沒有產生外部可見的副作用,所以foo()的整個方法體都可以被優化為空。經過dx工具處理后,Dalvik版程序相對JVM版確實是稍微優化了一些,不過沒有影響程序的語義,程序的正確性是沒問題的。這是其一。?

    其二是Dalvik版代碼只要多分配一個虛擬寄存器就能在return-void前同時持有a、b、c三個變量的值,指令幾乎沒有變化:?
    Dalvik bytecode代碼??
  • 0000:?const/4??????v0,?#int?1?//?#1??
  • 0001:?const/4??????v1,?#int?2?//?#2??
  • 0002:?add-int??????v2,?v0,?v1??
  • 0004:?mul-int/lit8?v2,?v2,?#int?5?//?#05??
  • 0006:?return-void??

  • 這樣比原先的版本多使用了一個虛擬寄存器,指令方面也多用了一個單元(add-int指令占2單元);但指令的條數沒變,仍然是5條,數據移動的次數也沒變。?

    題外話1:Dalvik VM是基于寄存器的,x86也是基于寄存器的,但兩者的“寄存器”卻相當不同:前者的寄存器是每個方法被調用時都有自己一組私有的,后者的寄存器則是全局的。也就是說,概念上Dalvik VM字節碼中不用擔心保護寄存器的問題,某個方法在調用了別的方法返回過來后自己的寄存器的值肯定跟調用前一樣。而x86程序在調用函數時要考慮清楚 calling convention ,調用方在調用前要不要保護某些寄存器的當前狀態,還是說被調用方會處理好這些問題,麻煩事不少。Dalvik VM這種虛擬寄存器讓人想起一些實際處理器的“寄存器窗口”,例如SPARC的 Register Windows 也是保證每個函數都覺得自己有“私有的一組寄存器”,減輕了在代碼里處理寄存器保護的麻煩——扔給硬件和操作系統解決了。 IA-64 也有寄存器窗口的概念。?
    (當然,Dalvik VM與x86的“寄存器”一個是虛擬寄存器一個是真實硬件的ISA提供的寄存器,本來也不在一個級別上…上面這段只是討論寄存器的語義。)?

    題外話2:Dalvik的.dex文件在未壓縮狀態下的體積通常比同等內容的.jar文件在deflate壓縮后還要小。但光從字節碼看,Java字節碼幾乎總是比Dalvik的小,那.dex文件的體積是從哪里來減出來的呢?這主要得益與.dex文件對常量池的壓縮,一個.dex文件中所有類都共享常量池,使得相同的字符串、相同的數字常量等都只出現一次,自然能大大減小體積。相比之下,.jar文件中每個類都持有自己的常量池,諸如"Ljava/lang/Object;"這種常見的字符串會被重復多次。Sun自己也有進一步壓縮JAR的工具,Pack200,對應的標準是 JSR 200 。它的主要應用場景是作為JAR的網絡傳輸格式,以更高的壓縮比來減少文件傳輸時間。在 官方文檔 提到了Pack200所用到的壓縮技巧,?
    JDK 5.0 Documentation 寫道 Pack200 works most efficiently on Java class files. It uses several techniques to efficiently reduce the size of JAR files:?
    • It merges and sorts the constant-pool data in the class files and co-locates them in the archive.
    • It removes redundant class attributes.
    • It stores internal data structures.
    • It use delta and variable length encoding.
    • It chooses optimum coding types for secondary compression.
    可見.dex文件與Pack200采用了一些相似的減小體積的方法。很可惜目前還沒有正式發布的JVM支持直接加載Pack200格式的歸檔,畢竟網絡傳輸才是Pack200最初構想的應用場景。?

    再次提醒注意, 上面的描述是針對概念上的JVM與Dalvik VM,而不是針對它們的具體實現 。實現VM時可以采用許多優化技巧去減少性能損失,使得實際的運行方式與概念中的不完全相符,只要最終的運行結果滿足原本概念上的VM所實現的語義就行。?

    ===========================================================================?

    上面“簡單”的提了些討論點,不過還沒具體到JavaScript引擎,抱歉。弄得太長了,只好在這里先拆分一次……有些東西想寫的,洗個澡又忘記了。等想起來再補充 orz?
    “簡單”是相對于實際應該掌握的信息量而言。上面寫的都還沒撓上癢癢,心虛。?
    Anyway。根據拆分的現狀,下一篇應該是討論動態語言與編譯的問題,然后再下一篇會看看解釋器的演化方法,再接著會看看JavaScript引擎的狀況(主要針對V8和Nitro,也會談談Tamarin。就不討論JScript了)。?

    關于推薦資料,在 “我的收藏”的virtual machine標簽 里就有不少值得一讀的資料。如果只是對JavaScript引擎相關感興趣的話也可以選著讀些。我的收藏里還有v8和tamarin等標簽的,資料有的是 ^ ^?

    能有耐心讀到結尾的同學們,歡迎提出意見和建議,以及指出文中的錯漏 ^_^?
    不像抓到蟲就給美分的大師,我沒那種信心……錯漏難免,我也需要進一步學習。拜托大家了~?

    P.S. 畫圖真的很辛苦,加上JavaEye的帶寬也不是無限的……所以拜托不要直接鏈接這帖里的圖 <(_ _)>?
    有需要原始圖片的可以跟我聯系。我是畫成多幀PNG然后轉換為GIF發出來的。上面的PNG圖片都還保留有原始的圖層信息,要拿去再編輯也很方便 ^ ^?

    更新1:?
    原本在樹遍歷解釋器圖解的小節中,我用的是這幅圖:?
    ?
    其實上圖畫得不準確,a、b、c的右值不應該畫在節點上的;節點應該只保存了它們的左值才對,要獲取對應的右值就要查詢變量表。我修改了圖更新到正文了。原本的圖里對i的賦值看起來很奇怪,就像是遍歷過程經過了兩次i節點一般,而事實不是那樣的。

    總結

    以上是生活随笔為你收集整理的虚拟机(基于栈还是基于寄存器)之谈的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    中文字幕字幕中文 | 久久人人爽人人人人片 | 人人干狠狠干 | 日韩有码专区 | 国产激情小视频在线观看 | 操操操夜夜操 | 在线国产一区二区三区 | 精品国内自产拍在线观看视频 | 国产精品理论在线观看 | 人人超碰97| 日韩激情视频 | 国产一区二区在线免费播放 | 91看片一区二区三区 | 久草视频在线观 | 玖玖综合网 | 久久视频在线免费观看 | 成 人 黄 色视频免费播放 | 亚洲精品国产品国语在线 | av中文字幕免费在线观看 | 蜜臀久久99精品久久久酒店新书 | www成人av | 久久99精品久久久久久久久久久久 | 国内精品久久久久久久影视简单 | 狠狠狠干狠狠 | 黄p网站在线观看 | 在线精品视频免费播放 | 欧美日韩久 | av网站在线观看免费 | 国产一区二区三区免费在线 | 国产69精品久久久久久 | 免费在线观看的av网站 | 香蕉网在线观看 | 欧美日韩一级久久久久久免费看 | 久久嗨 | 中文字幕在线免费看 | 欧美色精品天天在线观看视频 | 国产欧美综合在线观看 | 欧美性成人 | 国产一二区视频 | 激情网站| 亚洲电影网站 | 午夜精品福利在线 | 久久久99精品免费观看 | 在线综合 亚洲 欧美在线视频 | 亚洲成熟女人毛片在线 | 久久精品视频观看 | 射久久久| 国产明星视频三级a三级点| 夜夜躁天天躁很躁波 | 成人黄色电影免费观看 | 国产精品久久综合 | 久久精品久久精品久久 | 操碰av| 韩日在线一区 | 免费在线激情电影 | 99在线热播精品免费99热 | 久久久久久久久久久高潮一区二区 | 91在线视频免费观看 | 国产精品视频永久免费播放 | 丰满少妇一级 | 日本中文在线播放 | 欧美性大战 | 干干夜夜 | 综合网天天色 | 香蕉影院在线播放 | 国产丝袜高跟 | 国产精品久久精品 | 亚洲开心激情 | 久久久久久网 | 黄色1级毛片 | 日韩中文在线观看 | sesese图片 | 黄色成人影院 | 一区二区视频在线看 | 在线看黄色的网站 | 日韩av高清在线观看 | 亚洲年轻女教师毛茸茸 | 最新国产精品亚洲 | 国产精品美女视频网站 | 久久夜色精品国产欧美一区麻豆 | 日产乱码一二三区别免费 | 国产成人精品久久二区二区 | 免费在线国产视频 | 黄色免费av | 午夜视频播放 | 欧美大荫蒂xxx | 成 人 免费 黄 色 视频 | av中文字幕在线免费观看 | 国产精品久久久久久999 | 在线成人一区二区 | 国产一区免费看 | 欧美在线一二区 | 97小视频| 国产 字幕 制服 中文 在线 | 中文字幕频道 | avcom在线| 视频在线91| 久久这里精品视频 | 免费在线观看av网站 | 免费在线电影网址大全 | 亚洲欧美日韩在线一区二区 | 天天草天天操 | 午夜少妇av | 日本福利视频在线 | 中文理论片 | 91人人澡人人爽人人精品 | 欧美 亚洲 另类 激情 另类 | 免费网站黄色 | 久久免费国产电影 | 欧美作爱视频 | 日韩r级电影在线观看 | 四川妇女搡bbbb搡bbbb搡 | 中文字幕 影院 | 91chinesexxx | 视频国产精品 | 视频在线观看91 | 亚洲精品久久视频 | 极品嫩模被强到高潮呻吟91 | 国产在线观看99 | 日本三级不卡视频 | 九色视频网站 | 国产精品美女免费看 | 丁香综合激情 | 人人艹人人 | 丁香久久综合 | 国产男女爽爽爽免费视频 | 天天综合久久 | 天天干com| 99免费在线播放99久久免费 | 91看片在线免费观看 | 久久国产一区 | 国产精品igao视频网网址 | 久草在线手机视频 | 中文av字幕在线观看 | 97视频久久久 | 99精品视频在线播放观看 | 国产精品视频内 | 天天鲁一鲁摸一摸爽一爽 | 中文字幕影片免费在线观看 | 天天干天天想 | 四虎国产永久在线精品 | 久久久久看片 | 18女毛片 | 亚洲欧美经典 | 成年人黄色免费网站 | 91日韩在线视频 | 色婷婷狠狠五月综合天色拍 | 天天操天天操天天操天天操天天操 | 亚洲精品视频免费在线观看 | 狠狠躁夜夜躁人人爽视频 | 中文字幕av日韩 | 成人午夜电影久久影院 | 中字幕视频在线永久在线观看免费 | 91精品国产91久久久久久三级 | 91丨porny丨九色 | 婷婷午夜 | 黄色成人在线网站 | 国产精品一区二区你懂的 | 欧美大片aaa | 久久婷婷视频 | 国产精品久久网 | 久久久伊人网 | 蜜臀久久99精品久久久酒店新书 | 久久免费精彩视频 | 天天干天天操天天射 | 日韩三级免费 | 在线有码中文字幕 | 麻豆精品传媒视频 | 久久久www免费电影网 | 欧美日韩精品综合 | 国产精品久久久久毛片大屁完整版 | 国产精品综合av一区二区国产馆 | wwwwww黄 | 国产专区免费 | 色综合中文综合网 | 韩日视频在线 | 日韩电影在线观看一区二区三区 | 天天视频亚洲 | 亚洲国产片色 | 中文字幕 成人 | 亚洲色影爱久久精品 | www.亚洲视频 | 在线视频黄| 四虎影视成人永久免费观看亚洲欧美 | 中文字幕永久 | 久99久中文字幕在线 | 婷婷在线色 | 久久久久北条麻妃免费看 | 久久婷婷丁香 | 免费午夜视频在线观看 | 亚洲专区 国产精品 | 日韩av在线高清 | 成人日批视频 | 亚洲伊人天堂 | 国产视频精品久久 | 国产看片网站 | 人人看人人做人人澡 | 国产丝袜高跟 | 91中文字幕在线播放 | 亚洲精品中文在线 | 亚洲综合在线观看视频 | 亚洲精品1234区 | 毛片二区 | 在线激情网 | 国产精品美女免费看 | 在线播放精品一区二区三区 | 久久黄色影视 | 最近2019中文免费高清视频观看www99 | 成人av片免费看 | 天天草综合网 | 天天躁天天操 | 成在人线av | 97超碰中文 | 久久久久久在线观看 | 五月婷婷综合色拍 | 国产美女免费视频 | 久久草在线免费 | 亚洲人在线视频 | 97色免费视频 | 在线看v片成人 | 免费视频一区二区 | 又色又爽又黄 | 黄色片免费在线 | 美女视频免费一区二区 | 色婷婷播放 | 国产精品手机在线观看 | 在线亚洲午夜片av大片 | 91视频在线免费下载 | 欧美一级大片在线观看 | 在线观看 亚洲 | 久久久久一区二区三区 | 夜夜操天天 | 久草影视在线 | 一区在线免费观看 | 亚洲在线视频免费观看 | 成人久久18免费 | 免费看毛片网站 | 日本激情视频中文字幕 | 在线观看免费 | 一区二区视频免费在线观看 | 狠狠干网站 | 国产在线一区二区三区播放 | 狠狠干在线播放 | 中文字幕在线日 | 天天干夜夜夜 | 成人丁香花 | 久久精品亚洲一区二区三区观看模式 | 嫩嫩影院理论片 | 国产自产在线视频 | 欧美精品久久天天躁 | 日韩精品不卡在线观看 | av青草 | 五月婷婷在线综合 | 黄色av播放 | 中文字幕一区2区3区 | 9在线观看免费 | 久久一区二区免费视频 | 国产色黄网站 | 国产在线视频资源 | 久久久久久久久久伊人 | 天天色天天射天天综合网 | 97人人超碰在线 | 97福利在线 | 欧美日韩国产精品一区二区 | 亚洲区二区 | 国产高清av | 日日夜夜骑 | 麻豆一精品传二传媒短视频 | 亚洲综合在线五月 | 日日夜夜天天综合 | 午夜精品999| 午夜视频一区二区 | 日韩动态视频 | 99理论片 | 日韩激情精品 | 国产大片免费久久 | 欧美日韩免费网站 | 午夜精品久久久久久久久久 | 国产小视频在线免费观看 | 手机看片中文字幕 | 天堂在线一区二区三区 | 天天色天| 天天av天天 | 三级av在线播放 | 亚洲综合在线五月天 | 美女性爽视频国产免费app | 久久艹在线 | 久久精品国产精品亚洲精品 | 亚洲国产美女久久久久 | 久久99久久99精品免费看小说 | 黄色三级视频片 | 久久影院中文字幕 | 国产97色在线 | 91av资源在线 | 国产精品一区专区欧美日韩 | 婷婷伊人网| 国产在线永久 | 国产综合片 | 日本久久片 | 国产视频综合在线 | 五月婷久| 国产精品毛片久久久久久久久久99999999 | 五月天六月婷婷 | 国产看片网站 | 国产成人久久精品77777 | 日本久久久久久科技有限公司 | 午夜av日韩 | 免费在线观看av的网站 | 亚洲人成人在线 | 久久国产一区二区 | 欧美在线不卡一区 | 久久久久久免费网 | 99热精品在线观看 | 88av网站 | 午夜视频在线网站 | 久久免费a | 日韩精品一区二区三区电影 | 欧美日韩国产色综合一二三四 | 久久在草 | 日日夜夜亚洲 | 一区二区三区国产精品 | 91成人黄色| 特黄一级毛片 | 深爱婷婷 | 成人免费观看视频网站 | 久久天天躁狠狠躁夜夜不卡公司 | 天天色天天爱天天射综合 | 97超级碰碰碰碰久久久久 | 久久精品爱爱视频 | 美女性爽视频国产免费app | 九九免费在线看完整版 | 懂色av一区二区三区蜜臀 | 亚洲黄色三级 | 国产精品福利午夜在线观看 | 国产一级黄色免费看 | 成人久久精品 | 免费a级毛片在线看 | 色婷婷www| 激情久久小说 | 狠狠狠色丁香婷婷综合久久五月 | 精品视频999| 欧美日韩不卡一区二区 | 亚洲精品色 | 日本韩国精品在线 | 国产精品福利无圣光在线一区 | 久久国产露脸精品国产 | 久草资源在线观看 | 最近中文字幕免费av | 中文字幕成人一区 | 99精品免费久久久久久日本 | 欧美色888| 9热精品 | 国产欧美精品xxxx另类 | 叶爱av在线| 99视频国产在线 | 深夜国产福利 | 中文字幕免费国产精品 | 久久久国产精品亚洲一区 | 9热精品| 在线视频电影 | 精品无人国产偷自产在线 | 在线看片日韩 | 国产成人三级三级三级97 | 99视频在线精品免费观看2 | 亚洲视频每日更新 | 国产在线超碰 | 91av在线国产 | 黄色毛片网站在线观看 | 久久婷婷网 | 91在线成人 | 成人免费观看完整版电影 | 久久视频免费在线观看 | 国产最顶级的黄色片在线免费观看 | 国产色在线 | 91麻豆精品国产91久久久使用方法 | 日韩精品专区在线影院重磅 | 久久国产亚洲精品 | 黄色毛片视频免费观看中文 | 99视频在线看 | 亚洲成人av一区二区 | 欧美日韩免费一区二区 | 麻豆成人精品 | 久久看毛片 | 久久av在线播放 | 久热国产视频 | 热久久视久久精品18亚洲精品 | 免费av小说 | 久久精品一区二区三区国产主播 | 久久亚洲影院 | 国产精品6999成人免费视频 | 在线高清一区 | av福利在线播放 | 欧美日韩三级在线观看 | 国产免费作爱视频 | 亚洲久在线 | 久久免费激情视频 | 亚洲日韩欧美视频 | 成人国产一区 | 欧美一二三区在线播放 | 精品国产一区二区三区免费 | 亚洲视频aaa| 在线成人免费 | 福利视频一区二区 | 在线视频麻豆 | 欧美在线free| 午夜免费视频网站 | 一本一本久久aa综合精品 | 天天射,天天干 | 中文高清av | 91视频在线免费下载 | 婷婷激情站 | 天天色天天射天天综合网 | 日韩精品一区二区三区电影 | 国产成人久久久77777 | 天天艹天天操 | 最新久久免费视频 | 在线不卡视频 | 在线 欧美 日韩 | 久久久精品在线观看 | 国产麻豆精品免费视频 | 国产美女主播精品一区二区三区 | 国产护士hd高朝护士1 | 亚洲日本精品 | 日韩在线观看精品 | 午夜精品福利一区二区三区蜜桃 | 国产尤物视频在线 | 成人免费视频免费观看 | 成人毛片a| 久99久在线视频 | 久草在线一免费新视频 | 黄色免费观看视频 | 91精品网站在线观看 | 久久五月情影视 | 一区二区高清在线 | 国产麻豆剧果冻传媒视频播放量 | 国产麻豆剧传媒免费观看 | 91在线网站 | 久久综合加勒比 | 成人网在线免费视频 | 最近中文字幕免费大全 | 免费在线观看不卡av | 色欧美成人精品a∨在线观看 | av国产在线观看 | 国产在线播放不卡 | 国产96av | 国产视频首页 | 国产又粗又猛又爽又黄的视频免费 | 在线观看免费福利 | 国产明星视频三级a三级点| 正在播放五月婷婷狠狠干 | 欧美日韩亚洲第一页 | www最近高清中文国语在线观看 | 99精品在线视频播放 | 欧美精选一区二区三区 | 91亚洲精品久久久蜜桃网站 | 麻花传媒mv免费观看 | 欧美性色综合网站 | 深爱激情站 | 伊人激情网 | www.888.av| 伊人色综合久久天天网 | 精品国产伦一区二区三区观看体验 | 中文字幕视频播放 | 香蕉蜜桃视频 | 亚洲精品视频二区 | 日韩激情第一页 | 免费高清av在线看 | 黄网站色成年免费观看 | www视频免费在线观看 | 伊人永久在线 | 操操综合 | 五月亚洲| 日韩在线观看视频免费 | 超碰在线人人 | a视频在线| 性色av香蕉一区二区 | 亚洲午夜久久久久久久久久久 | 高清视频一区二区三区 | 亚洲人人网 | 久久视讯 | 久草网站 | 日韩一区二区三区在线观看 | 久草av在线播放 | 国内视频在线 | 国产黄色精品在线观看 | 欧美成人精品欧美一级乱 | 伊人影院在线观看 | 成人综合婷婷国产精品久久免费 | 人人爱在线视频 | 亚洲欧洲成人精品av97 | 国产色在线视频 | 亚洲专区视频在线观看 | av在线直接看 | av天天草 | 日韩女同一区二区三区在线观看 | 欧美日韩在线观看不卡 | 摸bbb搡bbb搡bbbb | 欧美一区二区三区在线看 | 亚洲精品视频免费看 | 久久久国产精品电影 | 成人动漫一区二区三区 | 在线观看一 | 亚洲国产欧美在线人成大黄瓜 | 国产专区第一页 | 又色又爽又激情的59视频 | 久久狠狠婷婷 | 91久久国产自产拍夜夜嗨 | 天天摸天天干天天操天天射 | 久久少妇av| 午夜精品一区二区三区可下载 | 久草视频免费播放 | 8x成人在线 | 免费在线观看国产精品 | 欧美激情视频一区二区三区 | av大片免费| 大型av综合网站 | 亚洲成人一区 | 国产一级视频免费看 | 毛片基地黄久久久久久天堂 | 日韩视频免费观看高清 | 国产精品99精品久久免费 | 国产精品女同一区二区三区久久夜 | 在线观看中文字幕一区二区 | 国产看片网站 | av中文资源在线 | 在线观看精品国产 | av超碰在线 | 亚洲精品乱码久久久久久蜜桃91 | 久久黄色免费观看 | 在线视频久 | 欧美精品免费视频 | 一区二区电影在线观看 | 美女视频黄在线 | 国产精品久久久久久久婷婷 | 国产日韩欧美在线一区 | 日韩深夜在线观看 | 亚洲免费在线观看视频 | 综合色爱| 人人射av | 激情婷婷av| 日韩一区二区免费在线观看 | av福利在线导航 | 99精品久久久久久久久久综合 | 国产精品18久久久 | 天天射天天爽 | 国产专区视频在线观看 | 天堂av网址 | 91在线网址 | 91精品福利在线 | 最近高清中文字幕 | 伊人小视频 | 久草在线中文视频 | 超碰97在线资源站 | 久久人人爽人人爽人人 | av日韩av| 国产精品一区二区三区免费视频 | 色综合久久久久综合 | 精品国产乱子伦一区二区 | 天天操天天操一操 | 奇米影视777四色米奇影院 | 美女黄频在线观看 | 精品嫩模福利一区二区蜜臀 | 在线看av的网址 | 日韩天堂在线观看 | 天天爽综合网 | 亚洲一区 影院 | 日韩精品免费一线在线观看 | 国产精品成人在线观看 | 精品国产一区二区三区久久久蜜臀 | 国产在线不卡 | 1024在线看片| 亚洲精品中文在线观看 | 日韩激情视频 | 久久视频在线观看免费 | 96av在线视频 | 91桃色在线播放 | av解说在线 | 亚洲精品午夜久久久久久久久久久 | 天天摸夜夜操 | 精品国产乱码久久久久久三级人 | 欧美国产日韩一区二区三区 | 在线视频日韩欧美 | 久久久影视 | 最近字幕在线观看第一季 | 亚洲精品女人久久久 | 久久综合九色综合欧美狠狠 | 久久99国产综合精品免费 | 天天射,天天干 | 免费看片在线观看 | 日韩丝袜 | 特级西西444www高清大视频 | 中文欧美字幕免费 | 久久久不卡影院 | 国产中文字幕在线免费观看 | 精品美女在线视频 | 亚洲第一av在线 | 99精品视频在线观看免费 | 六月丁香婷婷在线 | 日韩欧美一区二区三区黑寡妇 | 色婷婷六月 | 亚洲精品啊啊啊 | 久久香蕉一区 | 中文字幕在线播出 | 久久综合九色九九 | 精品1区2区| 欧美在线18 | 粉嫩av一区二区三区四区在线观看 | 久久夜色精品亚洲噜噜国4 午夜视频在线观看欧美 | 中文字幕一区二区三区视频 | 中文字幕电影一区 | 色综合亚洲精品激情狠狠 | 伊人电影在线观看 | 国产精品成人久久久久久久 | 午夜天天操| 97精品国产97久久久久久免费 | 久久婷婷色综合 | 91成人精品视频 | 9999精品免费视频 | 麻豆传媒视频在线免费观看 | 久久五月天综合 | 久久久精品国产免费观看同学 | 啪啪动态视频 | 日韩av不卡在线 | 亚洲 中文 欧美 日韩vr 在线 | 精品久久综合 | 免费裸体视频网 | www.午夜色.com | 一二三区在线 | 亚洲最新精品 | 亚洲成人av在线播放 | 在线国产中文字幕 | 成人91在线 | 九九久久免费 | 色婷婷久久久 | 国产视频精品在线 | aⅴ视频在线 | 国产精品毛片一区 | 国产成人三级在线 | 国产免费观看av | 99热精品免费观看 | 国产手机av在线 | 婷婷伊人网 | 网站在线观看你们懂的 | 国产精品1区2区3区 久久免费视频7 | 91成人黄色| 免费高清av在线看 | 在线观看国产福利片 | 中文字幕在线免费观看 | 人人澡人人草 | 欧美成人精品欧美一级乱黄 | 91网在线观看 | 免费看日韩 | 国产一区二区在线播放视频 | 蜜臀av在线一区二区三区 | 久久免费国产视频 | 性色av一区二区三区在线观看 | 国产精品99久久久久久宅男 | 成人在线视频在线观看 | 精品国产观看 | 69国产在线观看 | 久久在视频| 亚洲成av人影片在线观看 | 91精品国产福利在线观看 | 日韩精品久久一区二区三区 | 亚洲精品麻豆视频 | 黄网站色视频免费观看 | 国产aaa免费视频 | 久久国产精品精品国产色婷婷 | 婷婷在线精品视频 | 18国产精品白浆在线观看免费 | 狠狠的干狠狠的操 | 欧美最猛性xxx | 久久久久久久久久久精 | 久久免费福利 | 国产精品成人免费一区久久羞羞 | 国产一区二区三区 在线 | 午夜视频在线观看一区二区三区 | 国产日韩精品欧美 | 91刺激视频 | 日韩在线欧美在线 | 91伊人久久大香线蕉蜜芽人口 | 国产精品乱码一区二区视频 | 97在线影视 | 久久国产综合视频 | 亚洲精品久久久蜜桃 | 一级免费看| 久久久综合 | 国产福利av在线 | 免费看色网站 | 麻豆精品传媒视频 | 久久的色 | 九九热视频在线免费观看 | 国产精品国产亚洲精品看不卡 | 天天狠狠| 久久久三级视频 | 最新中文字幕在线观看视频 | 最新中文字幕在线播放 | 久久高清免费观看 | 亚洲精品久久久蜜臀下载官网 | 一本一道久久a久久精品蜜桃 | 91精品啪在线观看国产线免费 | 免费久久久 | 久久99中文字幕 | 成人网页在线免费观看 | 制服丝袜一区二区 | 欧美国产精品久久久久久免费 | 一级淫片在线观看 | 天天操天天怕 | 国产精品99久久久久久宅男 | 久草在线中文视频 | a在线视频v视频 | 婷婷在线色| 在线国产视频一区 | 在线免费观看国产精品 | 久久久一本精品99久久精品66 | 中文字幕丝袜一区二区 | 国内99视频 | 国产成人三级在线 | 色狠狠一区二区 | 麻豆视频大全 | 99在线精品视频在线观看 | 亚洲欧洲日韩 | 中文字幕亚洲欧美日韩2019 | 成人一级在线 | 一区二区三区观看 | 亚洲国产中文字幕 | 午夜久久福利 | 天天操天天添天天吹 | 五月婷网站| 国产麻豆剧传媒免费观看 | 久久av免费电影 | 国产原创av在线 | 国产专区精品 | 国产精品久久久久久久久久免费看 | 丁香激情综合 | 麻豆国产在线播放 | 五月天久久综合 | 在线观看中文字幕一区 | 99这里有精品 | 欧美一区二区三区在线 | 午夜123| 亚洲精品 在线视频 | 婷婷色在线观看 | 成人av免费看 | 黄色av成人在线 | 99精彩视频 | 久久久久99999 | www国产亚洲精品久久麻豆 | 亚洲色视频 | 久久字幕精品一区 | 在线草 | 色婷婷福利视频 | 久一久久 | 九九热精 | 欧美一区二区精品在线 | 国产精品中文久久久久久久 | 99在线免费视频 | 久久高清精品 | 69国产精品视频 | 久久久亚洲国产精品麻豆综合天堂 | 亚洲精品一区二区三区新线路 | 成年人国产在线观看 | 成人免费在线网 | 91亚洲精品久久久蜜桃网站 | 日韩亚洲国产中文字幕 | 欧洲精品码一区二区三区免费看 | 欧美aⅴ在线观看 | 欧美精品乱码久久久久久按摩 | 免费高清在线观看成人 | 久久久久黄色 | 99精品免费久久久久久日本 | 久草在线免费电影 | 这里只有精彩视频 | 奇米影视四色8888 | 在线草| 成人午夜影视 | 国产精品视频99 | 久久久黄色av | 国产一区高清在线 | 亚洲人成人99网站 | 在线免费黄网站 | 一本—道久久a久久精品蜜桃 | 午夜精品一二三区 | 天天躁日日躁狠狠躁av麻豆 | 日日骑 | 天天操天天射天天添 | 97色国产| 懂色av一区二区在线播放 | 成人黄色在线 | 国产欧美精品一区二区三区四区 | 超碰日韩 | 国产精品久久久久久久久久久久午夜 | 狠狠五月天 | 高清不卡免费视频 | 亚洲香蕉在线观看 | av中文在线 | 国产剧情一区二区在线观看 | 国内精品久久久久久久久 | 日本在线中文 | 999男人的天堂 | 在线观看日韩精品视频 | 欧美另类重口 | 在线免费视频一区 | 国产精品12 | 国产免费小视频 | 久久精品国产亚洲aⅴ | 97色综合 | 免费看污的网站 | 在线看成人av | 亚洲精品理论片 | 激情视频久久 | 亚洲国产高清在线观看视频 | 91成人免费在线 | 久久免费视频在线观看6 | 玖玖综合网 | 婷婷国产在线 | 深爱激情五月综合 | 久久夜色精品国产欧美乱极品 | 国内精品视频免费 | www.操.com| 成人在线观看免费 | 日本精品一区二区三区在线播放视频 | 精品国产一区二区三区不卡 | 中文字幕第 | 国产亚洲成av片在线观看 | 蜜臀久久99静品久久久久久 | 成人免费在线观看av | 日韩网站免费观看 | 日韩精品欧美精品 | 91精品久久久久 | 一区二区三区日韩在线观看 | 99在线视频播放 | 久久免费资源 | 韩国精品福利一区二区三区 | 8x成人免费视频 | 天天色天天上天天操 | 91在线麻豆 | 午夜视频在线观看网站 | 国产原创在线观看 | 九九免费视频 | 国产区精品在线 | 国产精品永久免费观看 | 久久人人添人人爽添人人88v | 中文字幕国产一区二区 | 看片在线亚洲 | 国产精品成人免费 | 日韩精选在线观看 | 中文字幕 第二区 | 九色自拍视频 | a级一a一级在线观看 | 亚洲一区二区91 | 欧美极度另类性三渗透 | a级免费观看 | 免费视频a | 成全免费观看视频 | freejavvideo日本免费 | 国语自产偷拍精品视频偷 | 成人a大片 | 亚洲波多野结衣 | 91黄色免费网站 | 黄色特一级 | 在线日韩中文字幕 | 日韩在线高清 | 婷婷av网站| 久久久久亚洲最大xxxx | 免费观看全黄做爰大片国产 | 蜜桃av久久久亚洲精品 | 免费国产一区二区视频 | 久久久久电影 | 成年人免费av | 成人超碰在线 | 91在线视频免费91 | 国产亚洲精品精品精品 | av片一区 | 成年人在线观看免费视频 | 国产女人40精品一区毛片视频 | 午夜精品区 | 国内免费久久久久久久久久久 | 色综合天 | 粉嫩av一区二区三区入口 | 夜夜操网站 | 就色干综合 | 国产精品久久久久久久久久白浆 | 射久久久 | 五月婷婷开心中文字幕 | 天天天天天天操 | 国产成人精品电影久久久 | 在线观看视频在线 | 精品国产欧美一区二区 | 国产资源网 | 国产成人黄色 | 国产精品乱码久久久久 | 麻豆精品在线 | wwwwwww黄| 免费日韩一级片 | 99久久99久久精品免费 | 精品久久99 | 日本韩国精品在线 | 五月在线视频 | 中文字幕一区二区三区四区久久 | 久久成熟| 91亚瑟视频 | 欧美视频二区 | 日本激情动作片免费看 | 激情婷婷丁香 | 97免费| 国产高清综合 | 日韩在线一二三区 | 亚洲黄色免费在线 | 在线免费高清 | 99自拍视频在线观看 | 国产成人精品久久二区二区 | 色婷婷色| 天天操天天干天天爱 | 观看免费av | 人人狠狠综合久久亚洲婷 | 亚洲成人动漫在线观看 | 亚洲精品乱码久久久久久9色 | 最新日韩在线观看视频 | 精品国产乱码久久久久久三级人 | av在线激情| 久久久国产精品亚洲一区 | 亚洲国产婷婷 | 国产精品嫩草在线 | 国产成a人亚洲精v品在线观看 | 亚洲精品视频免费观看 | 婷婷亚洲五月色综合 | 国产中文在线视频 | 日韩电影中文,亚洲精品乱码 | 在线观看国产www | 欧美日韩在线观看一区二区三区 | 91精品一区二区三区久久久久久 | 亚洲精品一区二区久 | 狠狠色综合欧美激情 | 香蕉在线观看视频 | 国产手机视频在线观看 | 国产小视频网站 | 天天做天天射 | 狠狠干,狠狠操 | 国产精品午夜久久 | av888.com| 三级av在线| 欧美精品乱码99久久影院 | 国产aa精品 | 国产一二三区av | 日本大片免费观看在线 | 91成人精品一区在线播放 | 99久久精品久久久久久动态片 | 亚洲精品福利在线观看 | 国产亚洲成av片在线观看 | 伊人国产在线播放 | 99精品视频在线看 | 婷婷中文字幕综合 | 成人免费看片98欧美 | www夜夜操 | 91丨九色丨91啦蝌蚪老版 | 日批视频在线播放 | 久久夜色精品国产欧美乱 | 日韩 精品 一区 国产 麻豆 | 国产精品99久久久精品 | 欧美成a人片在线观看久 | 欧美日韩在线第一页 | a在线观看国产 | 免费在线观看成年人视频 | 黄色软件视频大全免费下载 | 欧美巨乳波霸 | 国产一级免费播放 | www.久草视频 | 一级片免费视频 | 黄色小说网站在线 | 欧美大荫蒂xxx | 狠狠干2018 | 久久久亚洲网站 | 手机在线小视频 | 亚洲精品视频在线观看免费视频 | 99精品视频播放 | 在线欧美最极品的av | 久久综合给合久久狠狠色 | 在线不卡视频 | 色资源中文字幕 | 欧美一区二区三区特黄 | bbbbb女女女女女bbbbb国产 | 久久99日韩| 在线视频区 | 国外成人在线视频网站 | 成人av免费播放 | 在线视频日韩欧美 | 午夜精品一区二区三区视频免费看 | 国产免费午夜 | a天堂最新版中文在线地址 久久99久久精品国产 | 欧美日韩不卡在线视频 | 国产成人三级 | 激情久久五月 | 国产精品久久久av久久久 | 亚洲欧洲av| 国产亚洲va综合人人澡精品 | 婷婷av综合 | 欧美不卡视频在线 |