本發(fā)明涉及GPU微體系結(jié)構(gòu)、編譯器代碼生成技術(shù)和程序優(yōu)化技術(shù)領(lǐng)域,特別涉及一種逆向解析GPU指令的方法及系統(tǒng)。
背景技術(shù):
多年來(lái),GPU廠商只向用戶提供由驅(qū)動(dòng)封裝的上層API,而盡量少地暴露其內(nèi)部原理和細(xì)節(jié),如驅(qū)動(dòng)的軟件架構(gòu)、GPU的微架構(gòu)、指令集等。這導(dǎo)致學(xué)術(shù)界在GPU架構(gòu)研究領(lǐng)域,大幅落后于工業(yè)界,長(zhǎng)期處于停滯狀態(tài),在GPU只用于圖形加速的年代,這種保守策略在實(shí)際應(yīng)用中并不成為一個(gè)突出的問(wèn)題,甚至具有一定的合理性:最初的繪圖API與硬件實(shí)現(xiàn)強(qiáng)相關(guān),API只是硬件功能的簡(jiǎn)單封裝,API本身已經(jīng)暴露了足夠多的硬件細(xì)節(jié),早期版本的openGL接口更是被稱作三維繪圖的匯編語(yǔ)言;另外,繪圖API的直接調(diào)用者非常少,游戲都是基于渲染引擎開發(fā),顯卡廠商只要協(xié)助優(yōu)化好主流的渲染引擎,就可以保證絕大多數(shù)游戲的流暢運(yùn)行;從而,顯卡廠商可以更自由地改進(jìn)和創(chuàng)新微架構(gòu),無(wú)需在底層進(jìn)行后向兼容,只需在軟件封裝的繪圖API層次上后向兼容即可。
處于壟斷地位的GPU廠商N(yùn)vidia維持了技術(shù)封閉的慣性,不提供匯編器,不支持最底層的匯編編程,也不公開那些只在匯編層次上才能控制的硬件架構(gòu)特性,作為補(bǔ)充,它提供了底層接口PTX,雖然是和匯編很接近的中間表示,但PTX對(duì)硬件的控制能力要低于匯編,比如,PTX不能控制寄存器分配,也不能精確控制指令的調(diào)度行為,上層的類C接口對(duì)硬件的控制能力更弱,開發(fā)人員為了提升性能,只能寄希望于編譯器優(yōu)化,然而,“Daniel J Bernstein,Hsieh-Chung Chen,Chen-Mou Cheng,Tanja Lange,Ruben Niederhagen,Peter Schwabe,and Bo-Yin Yang.Usable assembly language for gpus:a success story.IACR Cryptology ePrint Archive,2012:137,2012.”指出Nvidia提供的編譯器NVCC生成的代碼效率并不高,比如寄存器分配存在大量bank沖突,實(shí)際上,Nvidia發(fā)布的許多并行算法庫(kù),都是基于內(nèi)部的匯編器,再進(jìn)行手工匯編優(yōu)化,才達(dá)到較為理想的效率,問(wèn)題是,不同于三維繪圖領(lǐng)域只有少量的渲染引擎開發(fā)商,GPGPU的用戶群廣泛而多樣,而Nvidia只對(duì)少量算法庫(kù)進(jìn)行了手工匯編優(yōu)化,只對(duì)少量大客戶提供了官方支持,其余大量用戶卻無(wú)法從昂貴的GPGPU硬件上榨取出最大的性能,這是對(duì)計(jì)算資源的巨大浪費(fèi),更糟糕的是,許多應(yīng)用極為廣泛的核心算法,Nvidia也并沒(méi)有優(yōu)化到位,比如單精度浮點(diǎn)矩陣乘(singe-precision matrix multiplication),Nvidia針對(duì)主流Kepler架構(gòu)的手工匯編優(yōu)化版,效率只達(dá)到理論峰值的74%,是第三方優(yōu)化的單精度浮點(diǎn)乘和NVIDIA廠商的cuBLAS中的SGEMM性能對(duì)比,第三方匯編優(yōu)化的比cuBLAS性能更高,這些研究表明匯編優(yōu)化對(duì)于挖掘GPU的性能很有價(jià)值。
一些研究者在GPU性能調(diào)優(yōu)和工具上有一些零散的進(jìn)展,比如微基準(zhǔn)測(cè)試程序“Zhang,Yao,and John D.Owens."A quantitative performance analysis model for GPU architectures."In 2011 IEEE 17th International Symposium on High Performance Computer Architecture,pp.382-393.IEEE,2011.”“Xinxin Mei,Kaiyong Zhao,Chengjian Liu,and Xiaowen Chu.Benchmarking the memory hierarchy of modern gpus.In Network and Parallel Computing,pages 144–156.Springer,2014.”“Henry Wong,Misel-Myrto Papadopoulou,Maryam Sadooghi-Alvandi,and Andreas Moshovos.Demystifying gpu microarchitecture through microbenchmarking.In Performance Analysis of Systems&Software(ISPASS),2010 IEEE International Symposium on,pages 235–246.IEEE,2010.”、匯編器和GPU匯編級(jí)別的優(yōu)化,然而,他們的工作都只集中在某個(gè)單一層面上,沒(méi)有提出一個(gè)能在新一代架構(gòu)上延續(xù)的通用技術(shù),比如指令破解方法和相應(yīng)的自動(dòng)化工具,另外GPU還沒(méi)有匯編級(jí)別的公開基準(zhǔn)測(cè)試程序,大部分正在使用的基準(zhǔn)測(cè)試程序都是基于CUDA,從而導(dǎo)致測(cè)試的結(jié)果并不可靠。
技術(shù)實(shí)現(xiàn)要素:
針對(duì)現(xiàn)有技術(shù)的不足,本發(fā)明提出一種逆向解析GPU指令的方法及系統(tǒng)。
本發(fā)明提出一種逆向解析GPU指令的方法,包括:
步驟1,將所述GPU指令進(jìn)行編譯,生成編譯文件,將所述編譯文件進(jìn)行反匯編,生成反匯編文件,通過(guò)匯編解析器將所述反匯編文件表示成instMap變量,其中所述instMap變量的變量類型包括操作碼、修飾碼、指令、操作數(shù)與對(duì)應(yīng)的操作數(shù)類型;
步驟2,將所述instMap變量輸入到解碼求解器,所述解碼求解器判斷所述instMap變量的變量類型,并通過(guò)已經(jīng)確定的所述操作碼或修飾碼查找相對(duì)應(yīng)的編碼。
若所述解碼求解器分別對(duì)所述instMap變量的64位編碼的每一位進(jìn)行探測(cè),再通過(guò)反匯編將所述64位編碼進(jìn)行反匯編,如果生成的新反匯編的指令與所述64位編碼原有指令的指令名稱不一樣,則說(shuō)明所述64位編碼當(dāng)前位表示操作碼,根據(jù)所述當(dāng)前位,對(duì)操作碼在編碼空間中進(jìn)行枚舉。
將所述instMap變量中指令的名稱與操作數(shù)類型作為關(guān)鍵字查詢visited字典,對(duì)于所述instMap變量里的每一條指令,探測(cè)除了操作數(shù)之外的其他位,返回已修改的某一位操作數(shù),將<指令,操作數(shù)類型>在visited字典里標(biāo)注為1,表示已經(jīng)訪問(wèn)過(guò)了。
通過(guò)將所述instMap變量中指令逐位進(jìn)行異或,通過(guò)修飾碼是否改變完成探測(cè)修飾碼,找到每條指令的修飾碼的編碼空間后,在修飾碼的編碼空間進(jìn)行枚舉,找出所有的修飾碼的名稱,然后根據(jù)某一修飾碼的名稱,找出所有帶所述某一修飾碼的指令的所有交集,最后將編碼與所有帶所述某一修飾碼的指令的操作碼的編碼做異或,獲取修飾碼的編碼。
根據(jù)操作數(shù)的名稱,獲取與其相對(duì)應(yīng)的編碼。
本發(fā)明還提出一種逆向解析GPU指令的系統(tǒng),其特征在于,包括:
生成變量模塊,用于將所述GPU指令進(jìn)行編譯,生成編譯文件,將所述編譯文件進(jìn)行反匯編,生成反匯編文件,通過(guò)匯編解析器將所述反匯編文件表示成instMap變量,其中所述instMap變量的變量類型包括操作碼、修飾碼、操作數(shù)與對(duì)應(yīng)的操作數(shù)類型;
查找編碼模塊,用于將所述instMap變量輸入到解碼求解器,所述解碼求解器判斷所述instMap變量的變量類型,并通過(guò)已經(jīng)確定的所述操作碼或修飾碼查找其余編碼。
若所述解碼求解器分別對(duì)所述instMap變量的64位編碼的每一位進(jìn)行探測(cè),再通過(guò)反匯編將所述64位編碼進(jìn)行反匯編,如果生成的新反匯編的指令與所述64位編碼原有指令的指令名稱不一樣,則說(shuō)明所述64位編碼當(dāng)前位表示操作碼,根據(jù)所述當(dāng)前位,對(duì)操作碼在編碼空間進(jìn)行枚舉。
將所述instMap變量中指令的名稱與操作數(shù)類型作為關(guān)鍵字查詢visited字典,對(duì)于所述instMap變量里的每一條指令,探測(cè)除了操作數(shù)之外的其他位,返回已修改的某一位操作數(shù),將<指令,操作數(shù)類型>在visited字典里標(biāo)注為1,表示已經(jīng)訪問(wèn)過(guò)了。
通過(guò)將所述instMap變量中與編碼相對(duì)應(yīng)的指令逐位進(jìn)行異或,通過(guò)修飾碼是否改變完成探測(cè)修飾碼,找到每條指令的修飾碼的編碼空間后,在修飾碼的編碼空間進(jìn)行枚舉,找出所有的修飾碼的名稱,然后根據(jù)某一修飾碼的名稱,找出所有帶所述某一修飾碼的指令的所有交集,最后將編碼與所有帶所述某一修飾碼的指令的操作碼的編碼做異或,獲取修飾碼的編碼。
根據(jù)操作數(shù)的名稱,獲取與其相對(duì)應(yīng)的編碼。
由以上方案可知,本發(fā)明的優(yōu)點(diǎn)在于:
發(fā)明可有效應(yīng)對(duì)GPU封閉技術(shù)體系而對(duì)編譯和程序優(yōu)化的限制:
1.由于NVIDIA沒(méi)有提供指令編碼,基于NVIDIA現(xiàn)有工具鏈,本發(fā)明提出的方法解析出GPU指令編碼,指令的基本格式如圖1所示。63~54表示操作碼,42~23表示20立即數(shù),21~18表示判定寄存器,17~10表示源寄存器,9~2表示目標(biāo)寄存器,1~0表示,具體的域與指令語(yǔ)法相關(guān);
2.在破解指令編碼的基礎(chǔ)上,結(jié)合PTX文檔,可構(gòu)造GPU匯編器;
3.可為GPU編譯器提供了一些編譯輔助功能,提高GPU程序的效率;
4.可設(shè)計(jì)和標(biāo)準(zhǔn)化一系列的微基準(zhǔn)測(cè)試程序來(lái)探測(cè)GPU微架構(gòu)特性和參數(shù)。
附圖說(shuō)明
圖1為指令的編碼格式示例圖;
圖2為指令解析算法流程圖;
圖3為操作數(shù)解法器算法(算法1)圖;
圖4為操作碼解法器算法(算法2)圖;
圖5為修飾碼解法器算法(算法3)圖。
具體實(shí)施方式
以下為本發(fā)明指令解析算法流程,如下所示:
指令解碼需要生成64位指令編碼和匯編指令的對(duì)應(yīng)關(guān)系,如圖2所示,算法流程如下:
首先利用PTX指令生成器,自動(dòng)生成NVIDIA PTX文檔中的所有指令及其修飾碼的組合,然后把這些PTX文件用ptxas編譯成cubin,并通過(guò)cuobjdump反匯編,最后把反匯編的信息,通過(guò)匯編解析器表示成instMap變量,用于解碼求解器的輸入,其中instMap的結(jié)構(gòu)包括:操作碼、指令、修飾碼、所有的操作數(shù)和對(duì)應(yīng)的操作數(shù)類型等。
操作數(shù)可以是寄存器(R5)、全局內(nèi)存([R6+0x20])、常量?jī)?nèi)存(C[0x2][0x40])、共享內(nèi)存([0x50])、立即數(shù)(0x9and1.5)和判定寄存器(P3),我們發(fā)現(xiàn),操作數(shù)的名字總是和數(shù)字相關(guān),因此可以推測(cè)操作數(shù)的編碼可以用它的名字來(lái)表示,比如寄存器操作數(shù)R5的二進(jìn)制編碼是101,立即數(shù)0x9的編碼是1001,相反,操作碼和修飾碼則是助記符,不能直接通過(guò)名字表達(dá)成二進(jìn)制,因此操作碼和修飾碼需要在他們的編碼空間進(jìn)行枚舉,我們發(fā)現(xiàn)修飾碼是指令相關(guān)的,同一個(gè)名字的修飾碼在不同指令的編碼也可能不同,比如LD和LDG的類型修飾碼名字都是.32,.64,.128,.S16,.U16,.S8,.U8,但是掩碼的位置卻不同,因此對(duì)于修飾碼,我們需要按照具體指令分別處理。
以下為本發(fā)明操作碼解法器算法,如圖3所示:
操作碼和修飾碼不能通過(guò)名字判斷其編碼,算法1展示了操作碼解法過(guò)程,根據(jù)NVIDIA提供的偽匯編PTX文檔,編寫PTX代碼,然后用ptxas編譯成cubin,再用cuobjudump反匯編,得到的匯編碼(instMap變量)作為算法1的輸入(1行),我們?cè)敿?xì)介紹下操作碼求解器的具體過(guò)程,對(duì)于反匯編文件里的每一行指令,分別對(duì)其64位編碼的每一位進(jìn)行探測(cè)(第9行),這里主要是通過(guò)反轉(zhuǎn)指令的每一位(11行),然后再通過(guò)反匯編工具nvdisasm進(jìn)行反匯編(13行),如果新反匯編的指令和原有指令的指令名稱不一樣(15行),說(shuō)明這一位表示操作碼,然后把這位保存在opBits里,得到這里位之后,可以對(duì)操作碼在編碼空間進(jìn)行枚舉。
通過(guò)編寫不同修飾碼的組合并進(jìn)行驗(yàn)證,進(jìn)一步得到修飾碼編碼,然而,由于NVIDIA提供的PTX文檔不全,這樣并不能保證找到所有的操作碼編碼和修飾碼編碼,通過(guò)算法1我們可以找出所有的指令。
以下為本發(fā)明操作數(shù)解法器算法,如圖4所示:
算法2是操作數(shù)解碼器的求解過(guò)程。首先建立一個(gè)字典(記為visited字典),字典的key為操作數(shù)所在指令的名字和操作數(shù)類型,value則標(biāo)記改操作數(shù)是否已被解碼。輸入是由操作碼求解器的輸入,操作數(shù)與操作數(shù)個(gè)數(shù),順序和類型和具體的指令相關(guān),因此我們標(biāo)記一組操作數(shù)是否被探測(cè),需要具體指令的名字和操作數(shù)類型(一個(gè)數(shù)組)作為關(guān)鍵字查詢visited字典,對(duì)于反匯編文件里的每一條指令(第4行),探測(cè)除了操作數(shù)之外的其他位(第8行),也是通過(guò)翻轉(zhuǎn)指令編碼中的一位(第10行)得到,wichChange返回修改的是哪一個(gè)操作數(shù),然后把這些位放到合適的數(shù)組里,把<指令,操作數(shù)類型>在visited字典里標(biāo)注位1,表示已經(jīng)訪問(wèn)過(guò)了(第18行)。
以下為本發(fā)明修飾碼解法器算法,如圖5所示:
修飾碼(Modifier),定義了某一條指令的具體行為,比如LD有類型修飾碼:.U8,.S8,.U16,.32,.64,.128,還有cache操作修飾碼:.CS(cache streaming),.CG(cache at global level)等。修飾碼相對(duì)操作碼來(lái)說(shuō)更為復(fù)雜,它的位置跨越很多操作位,而且與操作碼相關(guān),比如,同樣是類型修飾碼,LD和LDG的修飾碼在指令中的位置就不同,一種解決方法是通過(guò)把指令逐位地異或,然后觀測(cè)修飾碼是否改變來(lái)進(jìn)行探測(cè)(第6行到13行),找到每條指令的修飾碼的編碼空間后,在修飾碼的編碼空間進(jìn)行枚舉(第15行),下一步需要確定具體某個(gè)修飾碼的編碼,比如.U8的編碼,.S8的編碼,這個(gè)過(guò)程對(duì)應(yīng)代碼的20到29行,首先找出所有的modifier的名字(第20行),然后具體某一修飾碼的名字,找出所有帶這個(gè)修飾碼的指令的所有交集(23-25行),最后把這條編碼和這條指令的操作碼的編碼做異或,這樣就只留下修飾碼的編碼。
本發(fā)明還提出一種逆向解析GPU指令的系統(tǒng),包括:
生成變量模塊,用于將所述GPU指令進(jìn)行編譯,生成編譯文件,將所述編譯文件進(jìn)行反匯編,生成反匯編文件,通過(guò)匯編解析器將所述反匯編文件表示成instMap變量,其中所述instMap變量的變量類型包括操作碼、修飾碼、操作數(shù)與對(duì)應(yīng)的操作數(shù)類型;
查找編碼模塊,用于將所述instMap變量輸入到解碼求解器,所述解碼求解器判斷所述instMap變量的變量類型,并通過(guò)已經(jīng)確定的所述操作碼或修飾碼查找其余編碼。
若所述解碼求解器分別對(duì)所述instMap變量的64位編碼的每一位進(jìn)行探測(cè),再通過(guò)反匯編將所述64位編碼進(jìn)行反匯編,如果生成的新反匯編的指令與所述64位編碼原有指令的指令名稱不一樣,則說(shuō)明所述64位編碼當(dāng)前位表示操作碼,根據(jù)所述當(dāng)前位,對(duì)操作碼在編碼空間進(jìn)行枚舉。
將所述instMap變量中指令的名稱與操作數(shù)類型作為關(guān)鍵字查詢visited字典,對(duì)于所述instMap變量里的每一條指令,探測(cè)除了操作數(shù)之外的其他位,返回已修改的某一位操作數(shù),將<指令,操作數(shù)類型>在visited字典里標(biāo)注為1,表示已經(jīng)訪問(wèn)過(guò)了。
通過(guò)將所述instMap變量中與編碼相對(duì)應(yīng)的指令逐位進(jìn)行異或,通過(guò)修飾碼是否改變完成探測(cè)修飾碼,找到每條指令的修飾碼的編碼空間后,在修飾碼的編碼空間進(jìn)行枚舉,找出所有的修飾碼的名稱,然后根據(jù)某一修飾碼的名稱,找出所有帶所述某一修飾碼的指令的所有交集,最后將編碼與所有帶所述某一修飾碼的指令的操作碼的編碼做異或,獲取修飾碼的編碼。
根據(jù)操作數(shù)的名稱,獲取與其相對(duì)應(yīng)的編碼。