Recommend


1:Live Sports Results Premium
2:Taxilotse
3:ADVANCED CALLER CONTROL (paid)
4:PwnGameReader
5:Gamers Elite
6:ARent
7:LOTRO Elendir
8:Megan Fox Slide Puzzle Game
9:Giaru moji
10:حج الإيمان
11:The Dailey Method
12:Weekly
13:Paleo Festival Nyon Buddy
14:인천관광
15:Yamamah
16:Option Simulator
17:Delhi City Guide
18:PhoneMarks
19:monoQR
20:Breaking Sexual Addictions
21:RadioPanda
22:SK Calendar
23:TrueServe
24:二月河作品全集简繁
25:List-In-Hand® Mobile List
26:MDR Thüringen
27:RealTemp
28:Lucky Irish Live Wallpaper
29:Military - Defence Forces Ireland
30:Pokedex Companion

1:Ikemen Counter
2:マンガ無料アプリ★無料で読める小説コミックアニメ電子書籍
3:[無料占い]スマホの無料占いなら「占いステーション」
4:通販コスメ姫 ~激安カラコン編~
5:車・バイクアプリ
6:ビジネスホームスクリーン
7:[無料相性占い] 恋愛占い・復縁占い ☆Love Happy
8:EiWeight2
9:HBO GO Slovenia
10:HBO GO Serbia
11:HBO GO Hungary
12:HBO
13:HealthLog
14:Mobile TV German
15:Mobile TV French
16:The Best Movies Download
17:Mobile TV International
18:My-Cast Weather for Europe
19:TV za van
20:Accroids
21:Crackle for Sony Tablet
22:Girl's Secret
23:МТС Вторая память
24:링크모아 영화
25:凤凰星座
26:TUT.BY
27:MP3 Notes for Tablet
28:MP3 Notes
29:Task Alerts
30:Mobile Banking
maper

ARM的商業模式是如何煉成的?

ARM的商業模式是如何煉成的?

http://blog.csdn.net/szu030606/article/details/7580647

導讀:保守、嚴謹,又有一些皇族氣質,作為一家擁有純正英國血統的公司,ARM看似呆板的作風卻讓其在移動互聯網大潮中勢如破竹,沒有對手。也許過於看重產業鏈夥伴的聲音,導致ARM的決策有些遲緩,比如沒有更早的在中國設立研發中心。但這也許就是ARM與生俱來的基因,聽取生態系統中的夥伴的聲音,專注自己的事情。對於未來,ARM對伺服器市場充滿信心,在ARM總裁兼COO Tudor Brown看來,“ARM的技術太誘人了,Intel完全不在一個水準上。”電子工程世界對Tudor Brown進行了專訪,以下為全文:
三月中的某一天,我與ARM總裁兼COO Tudor Brown見面時,就拿出了自己的iPad並對他表示謝意:“如果沒有ARM,現在人們可能用不到這樣簡單有效的設備。”
在見面之前,我試圖打探一些關於Tudor的消息。讓我吃驚的是,作為一個英國人,Tudor在中國有著很多的好朋友,所以這次見面,我還有一個重要任務,就是為Tudor帶來老朋友們的祝福。畢竟,Tudor也許是以ARM總裁的身份最後一次參加中國媒體的訪談,因為在今年五月,他就要退休了。 在休息室,我與同行的一名資深記者閒聊,在ARM成立上海辦公室時,她曾對ARM進行過專訪,“記得我們去上海的時候,都不知道ARM是怎麼回事兒,也不知道ARM的商業模式。而現在,ARM的成功是顯而易見的。”
如何描述ARM
我一直在思考如何詮釋Tudor,詮釋ARM以及詮釋整條產業鏈。
讓我們先從一張老照片說起吧。

這張照片是ARM的第一次合作夥伴大會,此時距離ARM獨立還不足一年,右三為當時ARM CEO,前任IET主席Robin Saxby爵士。右一為ARM CTO Mike Muller,這位前惠普員工當時是作為系統架構負責人加入ARM,2000年10月至今一直擔任CTO。
坐在他們對面的是ARM的三個投資方,分別來自Acorn、Apple和VLSI。
不過Tudor並沒有在照片中出現,作為最早加入ARM的12位員工,Tudor 和Mike Muller,Pete Harrod以及Al Thomas負責公司的硬體設計,主要從事視頻及記憶體設計。

再來看另外一張老照片,這張照片出自ARM的官方微博,原話為:“猜猜ARM 12位創始人有多少仍在ARM?答案是6位!即使明年布朗先生退休也還剩5位。公司的凝聚力真的很重要。”另外我想補充下,實際上其他一些創始人儘管已不在ARM,但ARM卻是他們服務的最後一個公司。
我想,ARM的這種企業文化應該是源於其獨特的商業模式,在這種授權方式的商業模式下,只有凝聚全產業鏈的資源才可以做到,對於產業鏈如此,員工自身更是有著如此強大的凝聚力。
說到授權模式,我們可以看看當年ARM所設想的三種授權:

所以我向Tudor提出了這個問題:“ARM當時是如何想到這種商業模式的?”
Tudor :“這可能是從我很多年前的決定中得到啟發,當公司成立的時候我們清楚地知道ARM的技術是頂尖的。但是不要忘了我們是一家在英國的公司,而在英國是沒有龐大的半導體產業的,這種條件既是一種限制,同時也是一個創造新的商業模式的機會。當時我們想到了這樣一種模式,這個也是一個非常大的試驗,我們一旦確認了這是我們的目標後,我們就共同朝著這個目標去努力。在過程中ARM也在不斷地對這個業務模式進行調整,同時重新定義了公司的運營模式。這樣一種業務模式可能是要一個不太一樣的思維,和其他業務模式相比,它更傾向於在很多事情之間找到一個平衡點。如果你要找到最準確的平衡位置的話,是要不斷地去回顧我們取得的一些成績。”
Tudor以如何平衡授權費和版稅來舉例:“在一開始,我們決定一定要收授權的費用,用它來支撐公司運營的開銷。當我們的客戶取得成功以後版稅對我們來說就像一個獎勵機制,是公司能夠變得有盈利。其實當時我們已經做好了最壞的打算,即使我們永遠收不到版稅的費用的話,公司也是能夠靠授權費用生存下去。因為我們當時很清楚的認識到,我們的成功需要很多年時間。事實上ARM版稅收入直到1997年才有了明顯的增加。另外一個平衡,就是如何權衡利益歸屬,哪些是應該歸ARM所有,哪些是歸生態環境所有。如何平衡開發新產品與繼續使用舊產品之間的平衡。”
“所以在ARM裡,大家永遠都是在討論,甚至爭論,到底哪種結果才是最好的。這也是為什麼對於ARM來講,我們需要不停地聆聽客戶的需求。”Tudor表示。
20年前的SWOT分析
從一份廣泛流傳的關於ARM歷史的PPT中,我找到了20年前的SWOT分析,在這個20年前的分析中,明確的指出了ARM的優勢和劣勢。這是個很有趣的事,畢竟如果現在回過頭來看,很多分析現在仍然是正確的,而有些已然從弱勢變成了ARM的優勢。


EEWORLD冀凱:“如果讓您現在重新給ARM做一份SWOT分析,您會如何考慮?”
Todor :在過去的這些年裡我們制定了很多原則,如公司應該怎樣運行,員工應該怎樣表現,比如說我們的原則裡面有一條就是團隊合作比個人的單打獨鬥更重要,我們要對事情作出快速回應,要求員工積極主動為客戶著想等等。我相信即使我要離開ARM公司,這一套標準在未來的20年裡還是會存在的。
如果說到SWOT分析,我們現在處在一個最好的時代,機會最多,當然我們的弱點仍然會存在,這需要我們不斷去正視、有效地解決。任何一個公司都會面臨各種威脅,其中對於我們來說有一個威脅就是我們可能會把自己的未來想的過於美好,這個也是一個非常大的威脅。我不認為ARM是一個過於自滿的公司,但我們要小心,因為每一家企業都有可能很容易走到那一步。這就是我認為ARM面對的最大威脅。
Quora
ARM技術涉及幾乎所有的電子產業,而我也試圖邀請行業從業者提問,力圖從各角度完整的描述ARM。
某中國IP行業從業者以及某SoC晶片公司從業者:現在使用ARM架構,大家可以很好地降低研發成本,加快產品上市週期。但從智慧手機市場來看,現在好像陷入了軍備戰爭。晶片的生命週期變得越來越短,從購買(授權)一個核至推出晶片,再到最終推出終端產品,這個時間對於大部分廠商來說是否過長?如何保證廠商收回投資?
Todor :儘管看起來我們推出新產品的速度比較快,但這個問題並不能這麼簡單的看。我們有三個獨立的產品系列:A、R及M,以及我們的圖形產品Mali。這些產品授權給眾多公司或公司裡不同的設計團隊。以最流行的Cortex A系列來看,A9推出兩年半之後推出了A15,完全符合手持類消費電子產品更新週期。另外,儘管有一些技術市場領先的廠商可以在早期就推出新產品,但基於商業市場的種種原因,舊款產品持續時間很長。Cortex A9現在是最主流的高端移動處理器,但在ARM的所有產品出貨量中所占的比例依然相當少。我們出貨量最大的產品仍然是ARM7、ARM9及ARM11的授權。
另外需要注意的是,我們配合合作夥伴的產品開發計畫,推出最尖端的技術,這時,我們能夠聽到客戶更多的抱怨反而是研發進度過慢而不是過快。
飛思卡爾單片機市場經理曾勁濤:“ARM現在已經進入了伺服器、MCU等領域,未來ARM還打算進入哪個大的市場?ARM進入新市場的方式是通過自身成長抑或收購?”
Tudor:其實所有的市場對於ARM來說都是一樣的,我們的工作並不是針對某個市場去設計技術或產品,而是為一系列的應用提供一個合適的產品。事實上,的確在公司的歷史上,我們可能會針對部分細分的市場,比如五年前在MCU領域和現在的伺服器市場。但歸根結底,ARM的任務只是按照市場需求設計路線圖。
在MCU市場,我們需要推出更小的,功耗更低的產品,在伺服器市場我們則需要重點考慮產品性能,而對於手機市場,高性能,低功耗及安全性又都是重要的。與此同時,汽車電子要求安全及可靠性,當我們開發車用市場同時,發現工業或醫療設備中同樣需要該技術。所以對於ARM來說,哪怕我們只針對某個特定市場做開發,最後也可以變成普遍的需求。
關於第二個問題,ARM兩者都會考慮,縱觀ARM的歷史,物理IP及圖形IP兩塊業務都是通過收購得到的,我們更好地模式是通過收購之後,再通過自身使其成長,對於ARM來說,這是一個比較成功的模式。
QNX軟體公司中國南方部銷售經理哈駿元:“近年來,晶片廠商紛紛收購軟體作業系統公司, 從而向市場提供更完整的系統級解決方案,例如英特爾收購風河,網路處理器供應商Cavium收購Montavisa。請問ARM在軟體技術的投入上有何計畫?尤其是面對ARM新進入的細分市場,如網路通信,工控和汽車市場。”
Brown:其實有一些企業會在某些市場達到一個成熟的情況下,在這些市場尋找一些收購的機會,但是有時這種收購的意圖有時我們看得清,有時我們看的不是很清楚。ARM的戰略是非常簡單的,我們是希望能夠打造一個開放的生態環境,所以如果一家企業要來支援ARM的話,最好的辦法就是能夠讓它獨立地對ARM進行支持,而不是由我們所控制。所以ARM向來是希望是一個使能者(Enabler)而不是控制者,這也是為什麼現在我們有這麼強大的、數量眾多的一個生態系統。這個模式也得到了我們合作夥伴的認可。
中國軟體行業協會嵌入式系統分會副秘書長何小慶:“ARM從ARM7開始一直發展到現在,包括後來推出的64位元指令集直指伺服器市場,這樣一個從低端到高端的覆蓋的領域越來越寬泛,我的問題第一個是要支持這麼寬泛的應用,今天的ARM會不會面臨著比以往更大的技術挑戰和商業上的不確定性。”
Tudor:的確,我們現在的產品覆蓋範圍比以前廣了許多,但我們並不是為了開發而開發,也不是為了兜售新產品而不斷創新,ARM以市場需求的角度開發,傾聽來自授權合作夥伴方的需求,他們會告知ARM的需求,從而一起去定義未來的產品路線圖。不同的聲音不光來自半導體廠商,還包括OEM廠商、作業系統公司、大學研究院,當然也包括我們自己。通過這些討論,我們會探討如何設計一樣新技術,以滿足各方需求。
ARM的業務模式決定產品在多次授權的情況下才可以成功,也就意味著我們要為更多人提供有價值的技術。
某IT資深媒體人:“有人評論ARM進軍伺服器領域的難度不比intel進軍移動互聯領域的難度小,您如何看待?”
Tudor:首先要澄清的一點是並不是說ARM已經在伺服器市場,而是說我們的技術可以被用在也將會被用在這個市場。為什麼我們相信ARM將會被用在伺服器市場呢,是因為我們的解決方案能夠提供遠遠超過現有解決方案的功耗,在性能功耗方面我們提供的是非常好的解決方案。但可能需要一點時間,因為目前這個市場,大家可能對Intel的X86比較熟悉,所以需要花時間讓生態系統逐漸來熟悉不一樣的技術。但是因為ARM提供的技術實在是太誘人了,所以這件事是一定會發生的。所以,我覺得這個難度不可以直接對比,因為從一個用戶的角度來看的話,Intel對移動市場並不能提供什麼吸引人的東西,所以並不是一個類似的、可比的情況。
關於中國
Tudor經常造訪中國,對於中國半導體產業的發展做出了極大貢獻。中芯國際設計服務副總裁湯天申博士對於Tudor的評價非常之高,他表示:“ARM 與中芯國際有著長期的合作,我們欣慰地看到,在 Tudor Brown先生的領導下,ARM成長成為全球第一的IP公司,成為中芯國際的戰略性的合作夥伴。我本人與Tudor Brown先生有幾次非常深入的交談,我非常讚賞他對IP產業和中國晶片設計產業的深刻理解和深邃前瞻。”
瑞芯微電子是一家受益於ARM的晶片設計公司,其首席市場官陳鋒提問:“請問Tudor對於中國芯未來有什麼期望?”
Tudor:如果你問我中國芯的未來會怎樣,這個是沒有人知道的,但是我能看到的是現在中國有些公司未來是一定會取得成功的。有些中國的企業已經在特定的領域呈現出來自己的經驗或者是專長,我可以確定他們會在這些領域維持住他們市場領導的地位。中國又是一個不太一樣的國家或者說是市場,很多各種各樣的實驗都在進行。現在的中國和矽谷一樣,成功或者失敗每時每刻都在發生著。但即便失敗發生,會有一批人得到了豐富的經驗以及創新的想法,可能會對下一輪的經營帶來一些積極地作用。當然對於我來說,可以簡單地將中國未來描述成一片光明,但是一定會有一些挫折,這個是很正常的過程。
與此同時,我也看到一些中國企業很樂於或者說很有信心地去改變他們的經營模式,儘管他們做的這些改變會帶來一些不可知的結果。比如說山寨現象,沒有人在它出現之前會預言到會有這個現象,其實也給很多人帶來了益處,而且目前這種模式在其他領域也得到了成功複製。我個人的結論是,一定會有一些傳統的半導體行業企業會獲得成功,但在中國這片神奇的土地上有大量的在改變遊戲規則的企業,他們可能會帶來一些不一樣的結果,這可能會成為對其他企業的威脅。
總體來說這是個令人興奮的未來,沒有人預計它到底會怎樣發生,但是我很願意看到到底是怎樣一個結果。前面提到了業務模式,其實在中國我們的業務模式也一直受到一些挑戰,我們也會針對中國做出一些改變。包括我們中國的團隊經常把一些本土客戶的需求告訴ARM全球,總部會針對需求進行一些調整。
ARM中國區總裁吳雄昂在此補充道:半導體行業本身就是一個全球性的行業,但是從消費電子市場終端劃分來看,又分為本土市場和國際市場。終端市場的差異化在經過了移動互聯與雲計算之後被放大了,呈現出多樣性特點。產品的更新速度更快造成了軟體工作量變多,對於半導體行業來講,深層的難度和挑戰增加了。
同時反過來講,半導體產業鏈重新洗牌以後,中國企業開始有了機會,自信心得到了提高。而如果產業鏈要不洗牌,純粹照搬西方模式,可能80%的半導體公司是很難存活在下來的。中國的客戶自信心有兩個點,第一個因為有本土市場與國際市場差異化的問題,這個本土市場不光只針對中國,而是全球化的西方市場與發展中國家市場的區別。第二個是在過去十年以來中國企業看到純粹靠成本競爭不是一條出路,所以他們在幾年間下大力度開展平臺化建設。在移動和互聯網產業鏈變化之後,通過對市場的積累,可以再對產業鏈進行創新。比如說原來做電視的可以做機上盒,做機上盒的又可以轉做手機。
另外生態鏈也在變革著,就是我們過去所謂的生態鏈合作,必須通過一家主導的公司來做。而現在這條生態鏈打亂了,重整後的生態鏈跟過去完全不同。這也是我覺得為什麼中國企業信心這麼高,投資這麼多的原因。
關於未來
對於ARM以及Tudor的未來,對這個話題感興趣的不光是媒體、ARM的合作夥伴甚至包括ARM的員工:“對ARM公司今後的發展有沒有一個新的思路或者是想法。”
Tudor:我堅信ARM現在處在一個非常良好的發展勢頭,接下來仍然會推出更多的非常好的產品,給我們的日常生活帶來一些變化。回頭看我在ARM長達25年的職業生涯,對我們在這個過程中取得的成績我也是倍感欣慰的。儘管我們有很好的技術,我也對ARM的技術很自豪,但更讓我自豪的是我們創造的這種虛擬的業務模式,可能在行業裡也是獨一無二的。能夠做到這一點的很大的原因是更多的時候我們是在聆聽,而不是去指導別人。所以更多時候我扮演的是一個催化劑的角色。”
“有沒有在任期間有一些比較遺憾的或者是還沒有達成的目標或是心願?”
Tudor:的確,這二十多年的過程中有時有一些失望我們也經歷過,我們也經歷過一些失敗,但是這些都是比較小的,所以也不會說是真正的遺憾,但是如果一定要說的話,ARM這個公司有時在看到一些變化的時候反應速度可能有點慢,因為可能更多的時候我們習慣了去聆聽,聽了很多以後再來做決定,這個導致我們有時反應速度稍微慢了一些。另外我們可能應該更早地在中國做一些工程技術方面的工作,大家可能也知道ARM在上海有一個設計中心,有很多我們的工程師,可能我們現在再回頭看,更早一點啟動中國工程會好一些,因為中國的產業發展比我個人預期的要快很多。
“很想知道您退休以後最想去做什麼,怎麼去安排自己未來的生活。”
Tudor:我很清楚地知道現在是一個最好的時機離開ARM,由一個更年輕的團隊帶領ARM進入下一段旅程。但在這麼長一段時間ARM在我的生活中成為我生命最重要的一個部分,現在很難說以後要幹什麼,因為可能得等到我真的離開了才能想到自己以後要做什麼。我相信我的未來還會很活躍,但是說在什麼領域以什麼方式的話現在還不清楚。
採訪後記:
ARM的成功一項最重要的原因就是懂得聆聽,這也是為何我這篇文章幾乎都在使用Tudor的原話,儘管這麼一篇文章遠遠不夠紀念Tudor的一切功績,但我希望通過這種方式來原原本本的展現Tudor,因為這些話都可以看做他二十五年的提煉。
在我徵集問題時,得到了廣大網友和產業鏈同仁的熱情回應,這裡不一一答謝,我僅代表他們,希望Tudor今後一切順利。


中國出產拇指大小的Android 4.0電腦 售價500元人民幣

中國出產拇指大小的Android 4.0電腦 售價500元人民幣

http://www.xinpuit.com/content-6-1616-1.html

作者:simpleit
當全世界都在詬病Android分化和分裂,硬體開發商卻在自顧自的繼續出產各種型號的Android設備。還記得去年CSDN報導過的一家挪威公司出品的《僅重21克:USB大小雙核Android迷你電腦Cotton Candy》嗎?現在國人的驕傲來了:一款大小差不多但是更便宜的搭載Android 4.0的具備USB插口和HDMI埠(注意是port不是plugin)的代號“Model#MK802”稱為“Android 4.0 Mini PC”的設備,就是下圖中的這個。

圖片來源:CNX Software
當然這款設備並為搭載雙核CPU,而是基於AllWinner A10 CPU。CNX Software給出了比較詳細的資料:

CPU AllWinner A10 @ 1.5GHz + Mali 400 GPU
記憶體 512MB RAM
Storage 4GB Flash

microSD slot (Up to 32GB)

網卡 WiFi 802.11b/g
USB micro USB 2.0/OTG port

USB 2.0 Host port

鍵盤 Android虛擬鍵盤 或者 2.4G 無線鍵盤 + fly mouse
視頻輸出 HDMI (1080p)
支援的視頻格式 WMV/ASF/MP4/3GP/3G2M4V/AVI/MJPEG/RV10/DivX/VC-1/MPEG-2/

MPEG-4/H.263/H.264/1280*720P HD 30 fps, 1080P/720*480 D1 30fps

支援的音訊格式 AAC, AAC+, eAAC+, AMR-NB, AMR-WB, QCP, MP3, WMA, WAV, MIDI, M4A
作業系統 Android 4.0 (ICS)
電源輸入 5V/2A
機身尺寸 8.8*3.5*1.2cm
機身重量 200g

從給出的資料來看,比挪威的Cotton Candy重了不少。令根據liliputing提供的消息,該設備已經在AliExpress開售,價格$74美元。我從這些小東西身上看到了未來PC的雛形……

LLVM (Low Level Virtual Machine) 筆記

LLVM (Low Level Virtual Machine) 筆記

by loda

hlchou@mail2000.com.tw

很喜歡愛因斯坦所說的這段話,在科學上,每一條道路都應該走一走. 發現一條走不通的道路,就是對於科學的一大貢獻.科學史只寫某人某人取得成功,在成功者之前探索道路,發現此路不通的失敗者統統不寫,這是很不公平的 . 第一次看到LLVM技術時,當時會心想跨平台的技術,不是已經有了JAVA 虛擬機搭配JIT(Just In Time)技術,或是微軟所推的.Net 也基於支援IL Assembly可以在跨平台的方案上得到滿不錯的效能. 又一個新出的LLVM跨平台技術,真的能帶來跟以往不同的好處嗎?

在深入探究LLVM後,其實不論是在效能 (可以參考OpenBenchMark網站http://openbenchmarking.org/result/1204215-SU-LLVMCLANG23 ),支援語法的廣泛性(包括 C/C++ 都可以直接編譯為LLVM BitCode),並且也支援指標,函式指標,Inline Assembly,對於一般應用程式的轉換上,成本可以大幅度的降低,所產生的BitCode除了可以透過LLI(LLVM Interpreter)執行外,也可直接轉譯為所在平台的機械碼.對多數的開發者而言,光是可以讓C/C++開發的成果只要由開發者編譯一次後,就可以擁有跨平台的特性,對許多產品開發上就已經很有誘因了.

實際下載LLVM編譯安裝前,可以先透過http://llvm.org/demo/index.cgi 由LLVM所提供的線上測試網站,在這可以嘗試把不同的C/C++語言透過LLVM線上轉譯為對應的BitCode Assembly語法,而這樣的IR中間語法,就可以再透過LLC轉譯為不同平台上的原生Assembly Code.

LLVM是由Vikram Adve與Chris Lattne在2000年開始進行開發,透過編譯器的技術,可支援把C/C++,Object-C,Fortran,Java ByteCode,Python,ActionScript以及其他程式語言編譯為LLVM BitCode Assembly. 基於這跨平台的BitCode Assembly就可以再轉譯為目標平台的可執行機械碼. 並在2004年於 Code Generation and Optimization  (CGO’04)上發表了’LLVM: A Compilation Framework for Lifelong Program Analysis & Transformation ‘  Paper (文章可在這取得http://llvm.org/pubs/2004-01-30-CGO-LLVM.pdf, 投影片網址http://llvm.org/pubs/2004-03-22-CGO-LLVM-Presentation.ppt),介紹當時LLVM LifeLong Optimization 概念,並在這Paper中提到LLVM幾個主要的特色

1, RISC Like的指令集.

2, 以SSA(Static Single-Assignment) 形式提供數目不設限的虛擬暫存器

3, 以Load/store 指令存取型態定義的指標(Typed-Pointer)

4, 基於SSA可明確資料在運作過程中的傳遞流程

5, 提供跟語言無關的形態資訊

6, 在exception的支援上提供 setjmp/longjmp實作的Exception機制,並提供 invoke指令可呼叫一個需要帶有Exception Handler的函式,與提供Unwind指令,能透過Stack Frame回推到上一個invoke指令位置.

在2005年時,Chris Lattner加入了Apple,也藉此讓LLVM成為Apple官方所支持的編譯器方案.在2005年以前,LLVM一直沒有在實際的商業化產品中導入,直到2005年後,才開始應用在相關商業產品中.有關LLVM 技術在Mac OS X 上的演進可以參考以下的連結  http://arstechnica.com/apple/reviews/2007/10/mac-os-x-10-5.ars/11#llvmhttp://arstechnica.com/apple/reviews/2009/08/mac-os-x-10-6.ars/9 .

在支援C/C++的部份,LLVM的前端可以為llvm-gcc或是Clang.

以llvm-gcc來說,這是基於GCC修改而來支援 C/Object-C 的LLVM C Front End編譯器工具,並因此而擁有許多GCC故有的能力,llvm-gcc可用來產生最終的執行檔案,或是LLVM BitCode 二進位檔案,或是LLVM Assembly原始碼.llvm-gcc在不加入任何參數下的預設行為跟原本的gcc一樣,會產生最終的可執行檔案,如果是加上 -emit-llvm與-c則是會產生LLVM BitCode的二進位檔案,若加上-emit-llvm與-S則是會產生LLVM的Assembly原始碼.

既然已經有了llvm-gcc作為llvm的前端,又為何要有Clang呢? 可以參考這網頁上的訊息如下(http://linuxtoy.org/archives/llvm-and-clang.html )

Apple 使用 LLVM 在不支援全部 OpenGL 特性的 GPU (Intel 低端顯卡) 上生成代碼 (JIT),令程式仍然能夠正常運行。之後 LLVM 與 GCC 的集成過程引發了一些不快,GCC 系統龐大而笨重,而 Apple 大量使用的 Objective-C 在 GCC 中優先順序很低。此外 GCC 作為一個純粹的編譯系統,與 IDE 配合很差。加之許可證方面的要求,Apple 無法使用修改版的 GCC 而閉源。於是 Apple 決定從零開始寫 C family 的前端,也就是基於 LLVM 的 Clang 了。

由此可知Clang(官方網站為http://clang.llvm.org/)將會是未來LLVM所主要搭配配的前端C/C++編譯器工作.

LLVM本身主要是從處理器CPU的角度來進行虛擬化,也就是說讓各種語言所開發的應用程式都可以透過前端編譯器轉譯為LLVM虛擬處理器所能執行的BitCode,再將 BitCode轉譯為不同處理器平台的指令集(目前支援像是x86, ARM, MIPS,PowerPC,Sparc,XCore,Alpha…etc 處理器指令集).也因此LLVM Compiler只需要專注再LLVM對BitCode轉譯為不同處理器平台機械碼優化的任務上即可,而不需在前端去面對各種不同程式語言的編譯與優化工作.LLVM的前端包括像是llvm-lua(http://code.google.com/p/llvm-lua/)可以把lua編譯為LLVM BitCode,或是llvm-java支援的class2llvm要把Java ByteCode轉譯為LLVM BitCode.

LLVM 的跨平台支援演示.

如下筆者以同樣是ARM平台來看,對同一個 BitCode Assembly,選擇要對Cortex-A9優化與要對ARM9優化選項後,所產生的組語有哪些差異

[root@localhost reference_code]#

[root@localhost reference_code]# arm-none-linux-gnueabi-gcc -mcpu=arm9 sample_ar

m9.s -ldl -o sample_arm9

//BitCode Assembly 針對ARM 架構下的Cortex-A9處理器進行優化,並產生檔案到sample_cortexa9.s

#llc -O2 -march=arm -mcpu=cortex-a9 sample.bc -o sample_cortexa9.s

//BitCode Assembly 針對ARM 架構下的ARM9處理器進行優化,並產生檔案到sample_arm9.s

# llc -O2 -march=arm -mcpu=arm9 sample.bc -o sample_arm9.s

//可再透過 Diff 比較針對Cortex-A9ARM9優化後,兩者的差異 (各位可以自行嘗試,筆者就不再此列舉內容).

# diff sample_corei7.s sample_atom.s

兩者最直接的差異就是在所產生的組語中,Cortex-A9會加入對 ‘.fpu neon’ 指令集的支援,而這是在不支援Neon指令集的ARM9處理器上所不會有的.

最後不免要把兩段BitCode實際編譯驗證,

# arm-none-linux-gnueabi-gcc -mcpu=cortex-a9 sample_cortexa9.s -ldl -o sample_cortexa9

# arm-none-linux-gnueabi-gcc -mcpu=arm9 sample_arm9.s -ldl -o sample_arm9

並且比較基於兩個不同處理器優化下,所產生最終執行檔的Size差異.

# ls -l sample_cortexa9

-rwxr-xr-x. 1 root root 6877 May 13 23:42 sample_cortexa9

# ls -l sample_arm9

-rwxr-xr-x. 1 root root 7201 May 13 23:42 sample_arm9

筆者把一段C Code (由於重點是在演示如何把C Code轉 BitCode,再透過BitCode轉成不同平台的Assembly,所以在此就不偏重C Code的內容了),先轉成BitCode,之後再編譯為跨不同處理器平台的Assembly原始碼與最終的執行檔案,藉此演示LLVM在支援不同平台優化技術上的能力.

//透過clang C程式碼編譯為 BitCode 二進位檔案.

[root@localhost reference_code]# clang -O2 -emit-llvm sample.c -c -o sample.bc

[root@localhost reference_code]# ls -l sample.bc

-rw-r–r–. 1 root root 1956 May 12 10:28 sample.bc

//BitCode二進位檔案轉譯為x86-64 platform assembly code.

[root@localhost reference_code]# llc -O2 -mcpu=x86-64 sample.bc -o sample.s

//編譯轉譯後的assembly code 為 x86-64 native execution file.

[root@localhost reference_code]# gcc sample.s -o sample -ldl

[root@localhost reference_code]# ls -l sample

-rwxr-xr-x. 1 root root 8247 May 12 10:36 sample

//BitCode二進位檔案轉譯為 ARM Cortext-A9 platform assembly code.

[root@localhost reference_code]# llc -O2 -march=arm -mcpu=cortex-a9 sample.bc -o

sample.s

//編譯轉譯後的 assembly code 為 ARM Cortext-A9 native execution file.

[root@localhost reference_code]# arm-none-linux-gnueabi-gcc -mcpu=cortex-a9 sample.s -ldl -o sample

[root@localhost reference_code]# ls -l sample

-rwxr-xr-x. 1 root root 6877 May 12 10:54 sample

藉由上述的演示,我們可以知道LLVM如何透過LLC(LLVM Compiler)把同一段BitCode轉譯為ARM或x86平台的組語,而所產生的Assembly Code可以再透過GCC編譯為所在平台的執行檔. 有關LLVM的編譯環境運作示意圖,可參考下圖的所示.

透過LLVM前端把程式語言C/C++/Java/Fortran轉譯為BitCode Assembly,再藉由LLVM Compiler轉譯為不同平台差異的Assembly實作.

Clang的靜態分析語句引擎(static analyzer)

Clang不只是LLVM前端的C/C++/Objective C/C++ 編譯器工具,還支援對軟體開發極有幫助的靜態程式碼分析工具,有關LLVM的Clang靜態語句分析(Clang Static Analyzer)工具的介紹可以參考網站http://clang-analyzer.llvm.org/ ,這工具可用以分析C與Objective-C所開發的應用程式(尚不支援 C++/Objective-C++).目前這工具是以Open Source的方式釋出,並成為Clang 計畫的一部分.

其實市場上原本已有一些靜態程式語言分析的商業化工具,像是Coverity 或是 Klocwork,可在開發階段針對所撰寫的C/C++,Java應用程式潛在的設計問題提供分析結果,讓開發者針對這些分析內容先行解決,如此可減少在RunTime QA 人員找出Bug後,還要再提交給研發人員覆現問題的往返時間成本.好的程式語言靜態分析工具可以提前找出可能是在哪一行發生記憶體拷貝的溢位,避免QA週期透過窮舉法去走過所有程式邏輯的路徑,涵蓋範圍的不足.

在Clang基於LLVM環境編譯後,可以在llvm-3.0.src/tools/clang/tools/scan-build與llvm-3.0.src/tools/clang/tools/scan-view 這兩個路徑下取得scan-build與scan-view兩個Clang靜態分析工具編譯後的執行檔, scan-view 可用以檢視scan-build分析後產生到指定目錄中的結果報告.

接下來,筆者以如下的程式碼來驗證scan-build靜態分析程式碼的能力,

int main()

{

char *p,*xp;

char vBuffer[128];

int i;

p=malloc(128);

xp=(char *)malloc(222);

memset(p,256,0);

for(i=0;i<256;i++)

vBuffer[i]=0×00;

return 1;

}

這段程式碼中,筆者把malloc回來的pointer不作NULL檢查就直接使用,並刻意memset超出所配置記憶體的空間,或是透過 for 迴圈故意寫出超過Array配置大小的範圍,如下所示為透過scan-build執行後,產生的錯誤訊息

[root@www LLVM]# ~/scan-build  clang  -O3 -emit-llvm test.c -c  -I/usr/local/bin/../lib/clang/3.0/include/

test.c:9:2: warning: Value stored to ‘xp’ is never read

xp=(char *)malloc(222);

^  ~~~~~~~~~~~~~~~~~~~

1 warning generated.

scan-build: 1 bugs found.

scan-build: Run ‘scan-view /tmp/scan-build-2012-05-20-6′ to examine bug reports.

最後的報告是產生在  /tmp/scan-build-2012-05-20-6 目錄下,可以透過 scan-view工具進行檢視,如下指令.

[root@www LLVM]# ~/scan-view /tmp/scan-build-2012-05-20-7

檢視後的 Bug內容為

File:        test.c

Location:    line 11, column 2

Description: Value stored to ‘xp’ is never read

以scan-build對這案例的分析結果來看,只有找出 xp 變數有被配置但沒有被使用的問題,其他更嚴重的溢位問題,並沒有被偵測出來. 以目前scan-build的分析能力,對商業化產品的開發,選擇Coverity 或klocwork這類功能比較完整的靜態程式碼分析工具,應該會是對軟體品質確保上有比較好的幫助才是.當然,若在一般性的檢查上,scan-build還是可以帶來一些幫助的.

SSA(Static Single-Assignment)

LLVM IR (Intermediary Representation)會以 SSA (Static Single Assignment) 的形式表述,在往LLVM Assembly進一步的探究前,SSA應該是最值得介紹的項目,也是目前LLVM Assembly在設計實作上的基礎思維. 簡要來說,SSA的技術是由Wegman,Zadeck,Alpern,與Rosen在1988來開始發展,目前已經應用在GCC 4.0,IBM或Sun的Java JIT Compiler. SSA主要的概念為每個變數會被限制只能被給值一次的中間形式(IR),也就是說在轉成IR型態後,原本認為的變數,會在每次內容被改變時,就會重新把結果給值到一個新的變數中,例如變數X在運算過程中內容被改變了5次,就會因此而產生五個與最終給值有關的中間形式靜態單一分配形式.

每個描述式結果都會對應到一個全新的變數 (也就是說假設所在的環境中,全新變數宣告的總數也是沒有上限的.)

原本的描述式 轉成SSA後
x=a + b

y=b + x

x=a + 21

x=c * x

y=x + b

x1 = a + b

y1 = b + x1

x2 = a + 21

x3 = c * x2

y2 = x3 + b

經過SSA 的Dead Code Elimination後,編譯器就可以識別出其實x1與y1是沒有必要存在而可以加以優化消除的,簡化後的結果如下所示

x2 = a + 21

x3 = c * x2

y2 = x3 + b

LLVM Dalvik 執行環境的比較

從目前的趨勢上,LLVM很有機會在Android執行環境扮演一定程度的角色,相對於Dalvik Java 的執行環境,LLVM可以提供更貼近於平台Native應用程式的執行效能. 在跨平台的能力上,Dalvik可以選擇透過 Portable Interpreter (in C), Fast Interpreter (in Assembly)或基於Just-In Time Compiler編譯技術,在支援Neon指令集的平台上,JIT目前也能透過Neon指令產生優化後的結果.

而同樣的優勢,到了LLVM後又更進一步的改變,例如LLVM的IR(Intermediate Representation)是給LLVM直譯器/編譯器看的IR,相對於Java的ByteCode是一個被編譯後的結果,而JIT所做的優化則是根據每個Java ByteCode指令集操作改用原生指令實作所進行的優化(例如:把Dalvik move-wide指令用ARMv7 NEON指令實現,以進行加速),也就是說當從Java Code轉成ByteCode時,實際上有關應用程式流程的優化動作已經被進行過,所產生的ByteCode是適合直接上到Java處理器/虛擬機上執行的結果.

但,在LLVM透過Front-End所產生的結果則是一個還需要經過編譯的中間結果,並且是以SSA (Static Single Assignment) 編譯結果表述的內容,也就是說當BitCode要執行時,還需要透過LLI (LLVM Interpreter)或LLC (LLVM Compiler)來進行直譯或是編譯為目標平台原始碼的過程.

也因此,相較於Java JIT技術的結果,LLVM可以提供更接近於原生應用程式的效能,能根據基於LLVM IR所表述的SSA BitCode結果,重新優化編譯為目標平台的機械碼,相較於Java ByteCode的作法,是基於已經編譯好的ByteCode結果,把每個ByteCode指令改用以平台上的機械碼實現,LLVM能帶來的優化程度與結果是相對較佳的.

簡單來說,兩者最大的差異有

1, 從產生的執行碼來看: BitCode可以被LLVM編譯為原生碼,而且運作過程中直接呼叫Native函式庫,也可以直接被處理器執行.反觀Dalvik ByteCode,必須要基於Dalvik 虛擬機執行,就算是基於Dalvik JIT Compiler的技術,也只有部分的Dalvik ByteCode Trace-Run區塊可以被編譯為原生的機械碼,並不像是LLVM技術所產生的原生碼,是可以100%運作在原生碼執行的環境中.

2,從編譯後的原生碼重用性來看: LLVM可以把BitCode整個優化為原生碼,目前Dalvik ByteCode只支援執行時期階段的ByteCode JIT Compiler,一旦Dalvik應用程式結束,所有JIT編譯後的結果就消失了,必須要等下一次該Dalvik應用程式重新被載入執行,再根據每一個ByteCode Trace-Run區塊的Counter重新去決定哪些區塊要被編譯為Native Code.

3,從執行時期負荷來看 :LLVM Compiler後的結果,能以原生應用程式的方式執行,但Dalvik JIT會跟去Trace-Run Counter結果統計出熱區重新編譯,取得優化後的效能,對處理器的Run-Time負荷來說,LLVM顯然可以帶來更好的改善. 若Dalvik Application把主要的運算都放到JNI .so動態函式庫中,試圖改善Run-Time的效能問題,但卻會因為.so是有平台相依性的,而必須要針對所有的平台都提供一份專屬的.so.

4,從記憶體需求的角度來看:統計編譯後的結果,Clang把C/C++程式碼編譯為 LLVM IR之後透過LLVM Compiler轉譯為目標平台Assembly後所產生的執行檔大小,甚至可以比起直接透過GCC編譯的結果更佳,而Dalvik應用程式,在經過JIT後大小會膨脹 (一般來說為 4-8倍),並且相關的JAR Framework載入後,若也有相關的熱區,也會需要經由JIT技術來加以即時編譯優化,並儲存到JIT Cache中,相較於LLVM技術則無需有這段額外的記憶體成本在.

5,從儲存空間的需求來看: 一般的Dalvik Application APK需要有兩份儲存空間一個是DEX所在的.apk,一個是ODEX 儲存在dalvik-cache中.而LLVM Application並無這樣的必要.

6,從系統安全性的角度來看:LLVM支援指標/Inline Assembly,這是在Java世界中所不允許的,也因為支援指標,甚至是函式指標,可讓LLVM在效能的提升上得到很好的改善,但卻也隱藏了潛在的安全問題.

如下圖所示,為一個Dalvik應用程式在執行時相對於Dalvik 虛擬機與.so動態函式庫的示意圖,我們可以看到 Dalvik應用程式主要還是基於 ByteCode Based的JAR Framework,或是可直接透過JNI介面去呼叫外部的.so動態函式庫,以便得到接近原生碼的執行效能.

接下來,可以參考下圖為透過LLVM Interpreter 執行BitCode應用程式的示意圖,可以看到除了LLVM BitCode應用程式以外,其他外部函式的呼叫都會直接對應到原生的.so動態函式庫.

到這段落,各位以該對於Dalvik/LLVM應用程式在Run-Time上的差異有所了解了,接下來筆者將介紹另一個LLVM延伸的重要應用Native Client(Nacl) 與 Portable Native Client (PnaCl).

Native Client(Nacl) and Portable Native Client (PNaCl)

Google是在約2008開始進行Native Client的開發工作,並在2009年初舉辦Google Native Client Security/Hacking比賽 ,可參考網頁:http://www.zdnet.com/blog/google/hack-googles-native-client-and-get-8192/1295,比賽結果可以在這看到https://developers.google.com/native-client/community/security-contest/,並在Google 瀏覽器Chrome 10之後加入對Native Client的支援.

Native Client是類似微軟早期在Internet Explorer上支援的 ActiveX OCX元件的想法,讓網頁應用程式可以用處理器原生碼在支援這技術的瀏覽器直接執行. 參考Native Client網頁說明,簡單來說目標就是 "seamlessly execute native compiled code inside the browser",也就是可以 "在瀏覽器上無縫執行編譯後的機械碼 ". 目前Native Client編譯後的NaCl Executable (*.NEXE) 檔案格式,會根據目標平台編譯,例如筆者所使用的Windows 7 64bits電腦執行環境,所編譯出的NEXE檔案中的機械碼就會是是用於x86_64環境執行的程式碼,也就是說在開發Native Client時,所產生的NEXE檔案是沒有辦法直接跨到其它平台執行的.

基於Native Client SDK,一個Native Client技術的NEXE執行檔案可以透過如下i686-nacl-gcc 編譯指令編譯出來,如果透過i686-nacl-objdump 去觀察編譯後的結果,可以注意到每個函式的起點都必須是32bytes Alignment的記憶體位址.

i686-nacl-gcc -o hello_loda_x86_32.nexe hello_loda.c -m32 -O0 -g -pthread -O0 -g -Wno-long-long -Wall -lppapi -ldl

參考網頁https://developers.google.com/native-client/overview,Native Client,Native Client支援Software Fault Isolation (SFI),用以檢查所下載機械碼安全性的SandBox大約會讓帶來5%的Overhead.但由於Native Client是以機械碼的方式載入到使用者瀏覽器中執行,因此包括處理器的暫存器與支援inline assembly的寫法,都會比起透過Java Applet+Just-In-Time Compiler 或是透過Flash Action Script在瀏覽器中支援應用程式的方式來的更有安全性的疑慮. 舉個例子來說,這表示如果Native Client的Security SandBox如果沒有防守好的話,就有機會讓一個你在瀏覽網頁過程中所執行的Web Application讀取到你電腦上的檔案資料,或是有機會對將其他的惡意代碼寫入到你的電腦中,讓使用者電腦在不預期的狀況下,被第三方的應用程式給植入.

由於Native Client本身機制是產生出x86 32bits 與 64bits的機械碼,目前也不支援x86以外的平台,因此Google也在2010年時進行新的PNacl (發音為Pinnacle)技術開發,各位可以參考Google的’PNaCl Portable Native Client Executables’文件,如果要讓使用者根據不同平台的差異(X86-32/64 bits,Java,ARM,MIPS,PowerPC….etc)自行編譯出相關的執行檔案進行驗證無誤後發佈產品,這背後會有相當的難度,也因此,Google基於LLVM BitCode的特性開發了PNacl的技術,可參考筆者從文件’PNaCl Portable Native Client Executables’所截出的下圖,PNaCl的想法是開發者產出的是BitCode的檔案格式 (非原本Native Client的X86 32/64 bits ISA指令集),在網頁瀏覽的過程中,由使用者下載包含該BitCode內容的PNaCl檔案格式,透過LLC (LLVM Compiler)技術,動態的在目標平台上把BitCode轉譯為目標平台上的可執行機械碼,隨後成為一個NaCl執行檔案.

基於這樣的設計,等於可以延續Native Client計有的基礎,又可以讓產生機械碼的動作不是在開發者開發階段就要去面對不同平台差異而去產生,開發者所需面對的只有LLVM的BitCode,並且只要基於BitCode的環境驗證無誤下,就可以透過目前LLVM的優勢把BitCode重新編譯為目標平台上的機械碼,如此就可以延續目前Native Client的基礎,但又可以真正的達到跨所有處理器平台的目的.

下圖同樣為Google的’PNaCl Portable Native Client Executables’文件中的截圖,可以看到PNaCl的概念上,是透過LLVM BitCode達成跨平台的目的,在基於NaCl SandBox技術確保Native Client 執行環境的安全性.

Portable Native Client官方網頁為http://www.chromium.org/nativeclient/pnacl/building-and-testing-portable-native-client , 基於PNaCl所產生的執行檔為PEXE (原本的Native Client為NEXE),使用LLVM編譯器的好處是,開發者可以用C/C++語言開發,然後基於LLVM可以讓NACL應用程式在Browser上運作時,效率接近直接用C/C++語言針對該平台編譯的結果,而且最重要的是又可以用同一套LLVM產生中間碼(IL)的結果相容於所有的處理器平台.

Native Client定義跟Web Browser (例如目前Google的Chromium) 之間的介面為Pepper,可用以讓Native Client據此實作成為Browser Plug-in. Pepper介面是從Mozilla的NPAPI而來,新版的Native Client Pepper v2介面則重新在NPAPI基礎增加新的API介面.有關Pepper API的說明可以參考網頁http://code.google.com/p/ppapi/wiki/Concepts .

參考目前Native Client的文件,基於安全性的考量Native Client在施行上會有以下的限制存在

1,不支持Hardware Exception.

2, 不支持產生新的Process/Subprocess

3,不支持Raw TCP/UDP Sockets (會額外提供WebSockets 供TCP與UDP Peer-to-Perr Connection.)

4,不支持同步(Synchronous) Blocking I/O

5,不支持對可使用記憶體的查詢

6,可以使用Inline Assembly,但必需要通過Native Client Validator (ncval) 工具的查核

7,跟Native Client所 Plug-in進Browser介面的Pepper API呼叫必須從應用的Main Thread而來.

Google Native Client的相關資訊可以參考

Google Code Project http://code.google.com/p/nativeclient/
Native Client SDK https://developers.google.com/native-client/?hl=zh-TW
Download Native Client SDK https://developers.google.com/native-client/sdk/download?hl=zh-TW
Test Run https://developers.google.com/native-client/devguide/devcycle/running?hl=zh-TW
Distribute https://developers.google.com/native-client/devguide/distributing?hl=zh-TW
Getting Start https://developers.google.com/chrome/web-store/docs/get_started_simple?hl=zh-TW#step4

目前為止我們知道LLVM技術將有機會更廣泛的應用到瀏覽器技術上,與我們生活更緊密的結合,既然知道到LLVM本身的認識是可以把BitCode透過LLI (LLVM Interpreter)以直譯方式執行,或是透過LLC (LLVM Compiler)轉譯為不同平台上的機械碼,相對的把C/C++這些主要開發語言轉譯為BitCode的前端編譯器就變得很重要了,下一段落就讓我們實際操練Clang與LLVM的執行環境.

編譯第一個LLVM程式

接下來讓我們嘗試編譯一個基於BitCode檔案格式的LLVM程式,首先如下範例程式

#include <stdio.h>

int main()

{

printf("LLVM Test\n");

return 0;

}

透過clang -S -emit-llvm test.c -o test.llvm 進行編譯,可以產生 LLVM的Assembly Code,如下所示

; ModuleID = ‘test.c’

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128″

target triple = "x86_64-unknown-linux-gnu"

@gCount = global i32 0, align 4

@.str = private unnamed_addr constant [11 x i8] c"LLVM Test\0A\00″, align 1

define i32 @main() nounwind uwtable {

%1 = alloca i32, align 4

%i = alloca i32, align 4

store i32 0, i32* %1

%2 = call i32 (i8*, …)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0))

ret i32 0

}

declare i32 @printf(i8*, …)

有關LLVM Assembly Code格式的介紹可以參考http://llvm.org/docs/LangRef.html .

可以透過 clang -c -emit-llvm test.c -o test.bc,產生LLVM BitCode格式的檔案,並且可以透過lli執行所產生的 BitCode檔案,如下所示

[root@localhost test]# lli test.bc

LLVM Test

再來可以透過llvm-dis 反組譯 test.bc為test.ll

[root@localhost test]# llvm-dis test.bc

如下為test.ll內容

[root@localhost test]# more test.ll

; ModuleID = ‘test.bc’

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128″

target triple = "x86_64-unknown-linux-gnu"

@gCount = global i32 0, align 4

@.str = private unnamed_addr constant [11 x i8] c"LLVM Test\0A\00″, align 1

define i32 @main() nounwind uwtable {

%1 = alloca i32, align 4

%i = alloca i32, align 4

store i32 0, i32* %1

%2 = call i32 (i8*, …)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0))

ret i32 0

}

declare i32 @printf(i8*, …)

並可透過llvm-as test.ll 重新把test.ll編譯為test.bc.

[root@localhost test]# llvm-as test.ll

[root@localhost test]# ls -l test.bc

-rw-r–r–. 1 root root 656 Apr 10 22:49 test.bc

[root@localhost test]# date

Tue Apr 10 22:49:22 CST 2012

[root@localhost test]#

接下來讓我們以LLVM 把兩個BitCode檔案進行Link,以驗證LLVM的運作行為

首先 testA.c如下所示

#include <stdio.h>

long funcB();

int main()

{

int X=funcB();

printf("X:%xh\n",X);

return 0;

}

而 testB.c如下所示

#include <stdio.h>

long funcB()

{

return 0×9999999;

}

先把 testA.c與testB個別編譯為 testA.bc與testB.bc 兩個BitCode檔案格式,再透過llvm-link把這兩個BitCode檔案Link成一個test.bc檔案,再透過lli 執行該BitCode test.bc檔案.

[root@localhost test]# clang -c -emit-llvm testA.c -o testA.bc

[root@localhost test]# clang -c -emit-llvm testB.c -o testB.bc

[root@localhost test]# llvm-link testA.bc testB.bc -o test.bc

[root@localhost test]# lli test.bc

X:9999999h

[root@localhost test]#

如下所示,也可以先把 testB.bc先Archive 成一個libTestB.a 的靜態連結函式庫,並透過llvm-nm檢視該函式數所提供的Symbol,最後再透過llvm-ld把 testA.nc跟靜態函式庫libTestB.a 進行連結成Native執行檔案的動作,最後就可以透過 ./test驗證最終執行結果是否符合預期.

[root@localhost test]# llvm-ar rucs libTestB.a testB.bc

[root@localhost test]# llvm-nm libTestB.a

T funcB

[root@localhost test]# llvm-ld testA.bc -o test -lTestB

[root@localhost test]# date

Tue Apr 10 22:57:42 CST 2012

[root@localhost test]# ls -l test

-rwxr-xr-x. 1 root root 66 Apr 10 22:57 test

[root@localhost test]# ./test

X:9999999h =>符合預期.

[root@localhost test]#

在實際操練LLVM有關的工具指令後,接下來就是介紹LLVM 反組譯與BitCode檔案格式.

LLVM Assembly Language 與 程式碼反組譯

有關LLVM Assembly Language的支援列表筆者另外整理在http://loda.hala01.com/llvm-assembly-language/ ,供參考.

LLVM的Identifiers可以區分為Global全域開頭為’@'的Identifiers(包括函式與全域變數)與Local區域開頭為’%'的Identifiers,如前面的例子,所有的變數會以其命名作為字串但加上@或%表示其為全域變數(例如:@gX)或是區域變數(例如:%Y).參考’ LLVM Language Reference Manual’文件可接受的字元包括 ‘ [%@][a-zA-Z$._][a-zA-Z$._0-9]*’,如果有遇到不在這範圍內的字元,就會透過16進位的方式儲存,同樣以前述的例子來說,函式main的字串會以 ‘ main:%d\0A\00′方式儲存,長度為9bytes,其中\0A代表0x0A的16進位字元,而\00則代表0×00的字串結尾字元.

沒有在程式設計階段被命名的變數,就會以上述像是%11, @22,%33或@44這類數字形式方式來命名.

常數Constants的部份,筆者說明列舉如下所示

常數型態 說明
Boolean i1(a single-bit integer),

會以’true’ 與 ‘false’ 代表長度為1bit的整數值

Integer 通常為i32       (a 32-bit integer),可以用來表示負值,長度可為i1(1bit), i2(2bits), i3(3bits), … i8 (8bits), … i16 (16bits), … i32 (32bits), … i64 (64bits), …
Floating point 通常為half(16-bit floating point value),float(32-bit floating point value)或double(64-bit floating point value),表示的方式可以為10或16進位浮點數(double 0x432ff973cafa8000),或指數符號(例如1.23456e+2)
Null pointer 為Pointer Type,會以 ‘null’ 字串表示Null Pointer Constant
Structure 結構常數的組成會以{}括號來定義前後範圍,並以逗號’,'分隔前後的組成變數,每個組成變數都會包括它的形態(i32,float,i32*,double…etc),例如像是"{ i32 4, float 17.0, i32* @G }",其中’i32* @G ‘表示這個變數儲存的是全域變數 ‘@G’ 的位址,可用於透過存取這個結構時,再藉由這個變數去存取全域變數’@G’.
Array Array的組成會以[]方括號來定義前後範圍,並以逗號’,'分隔前後的變數值,每個變數都會包括它的形態,例如像是 "[ i32 42, i32 11, i32 74 ]",Array中常數的型態與變數個數,都需要跟原本所宣告的形態一致,例如像是’[11 x i32]‘(Array of 11 32-bit integer values)或’[4 x i8]‘ (Array of 4 8-bit integer values).
Vector Vector的組成會以<>的小於/大於括號來定義前後範圍, 並以逗號’,'分隔前後的變數值,每個變數都會包括它的形態,例如像是  "< i32 42, i32 11, i32 74, i32 100 >",Vector中常數的型態與變數個數,都需要跟原本所宣告的形態一致
Zero initialization 字串“zeroinitializer’ 可用以進行初始化值為零的任何型態(Type),通常應用在像是大型的Array,可以讓相關的變數初始化為Zero.
Metadata node A metadata node is a structure-like constant with?metadata type. For example: "metadata !{ i32 0, metadata !"test" }". Unlike other constants that are meant to be interpreted as part of the instruction stream, metadata is a place to attach additional information such as debug info.

而在變數的部分,當在程式設計階段,給定一個全域變數常數值時,這個全域變數所包含的常數內容,就可以在執行時期的任意時間點被參考與使用,可參考如下兩個全域32 bits Integer變數,與一個全域大小為2*32bits Array的宣告,初值的給予.

@X = global i32 17

@Y = global i32 42

@Z = global [2 x i32*] [ i32* @X, i32* @Y ]

在初值的部份,執行可以設定初值為’Undefined Values’,定義為Undefined Values的變數就表示應用程式並不在意該值的初始化內容,指定的方式可以為

i32 undef

store i32 undef, i32 *%1

筆者以如下程式碼作為例子,來進行編譯後的LLVM BitCode檔案格式解析與對反組譯BitCode Assembly的比對.

#include <stdio.h>

unsigned long long gW=10;

long gX;

unsigned int gY=30;

short gZ;

unsigned char gC=50;

short FuncBC(int vA)

{

int Y;

Y=((gC+gW+gX)*vA)+40;

Y*=gX+20;

printf("FuncBC:%d\n",Y);

return Y;

}

static unsigned long FuncA(int vA,int vB)

{

int Y;

gX=vA+vB;

gZ=gC+vB;

Y=gC*(gX+gY+gZ)+10;

Y*=gX+30;

gZ=FuncBC(Y);

printf("FuncA:%d\n",gZ);

return Y;

}

int main()

{

int Y=99;

int i;

for(i=0;i<99999;i++)

{

Y++;

}

Y=FuncA(Y,30);

printf("main:%d\n",Y);

return 0;

}

編譯這段範例程式

clang -c -emit-llvm test.c -o test.bc

進行反組譯

llvm-dis test.bc

進行BitCode檔案格式分析.

llvm-bcanalyzer -dump test.bc

首先從反組譯的程式碼來看,5個全域變數轉成BitCode後的內容如下所示

C中的變數宣告 BitCode產生的變數宣告
unsigned long long gW=10; @gW = global i64 10, align 8
long gX; @gX = common global i64 0, align 8
unsigned int gY=30; @gY = global i32 30, align 4
short gZ; @gZ = common global i16 0, align 2
unsigned char gC=50; @gC = global i8 50, align 1

可以知道,long長度為 64bits,int長度為32bits,short長度為16bits而char長度為8bits.預設的變數為unsigned,若屬於signed的變數則會加上common.沒有給予初值的全域變數預設值為0.

如下所示,定義為static function的話會加上internal,外部函式會以declare方式宣告原型,內部函式的定義會透過define,每個函式的函式參數會以型別跟參數名稱依序定義在函式參數中.

C中的函式宣告 BitCode產生的函式宣告
使用到外部呼叫printf 函式 declare i32 @printf(i8*, …)
int main() define i32 @main() nounwind uwtable
static unsigned long FuncA(int vA,int vB) define internal i64 @FuncA(i32 %vA, i32 %vB) nounwind uwtable
short FuncBC(int vA) define signext i16 @FuncBC(i32 %vA) nounwind uwtable

而以函式名稱的宣告來看,在這範例中筆者有宣告如下三個函式

short FuncBC(int vA)

static unsigned long FuncA(int vA,int vB)

int main()

在編譯為 BitCode後,會依據這三個函式名稱的長度,例如main為 ‘ main:%d\0A\00′,長度為9. (main:%d 長度為 7 bytes, 加上 0x0A 跟 0×00 各 1byte就為 9 bytes.)

@.str = private unnamed_addr constant [11 x i8] c"FuncBC:%d\0A\00″, align 1

@.str2 = private unnamed_addr constant [10 x i8] c"FuncA:%d\0A\00″, align 1

@.str1 = private unnamed_addr constant [9 x i8] c"main:%d\0A\00″, align 1

提到LLVM Assembly,最值得參閱的文件為’ LLVM Language Reference Manual’,可參考的網頁位置在http://llvm.org/docs/LangRef.html . LLVM Assembly目標在於成為一個 ‘Universal IR’,也就是可以滿足讓各種程式語言對應到的Assembly Code,不管開發者使用的是C/C++,Java,Python …等等,都可以透過LLVM的Front-End前端編譯器(例如 Clang)把這些開發的Source Code轉譯為IR Assembly,以便讓LLVM本身可以把所產生的IR程式碼重新編譯到最後所要執行的處理器平台上(像是 x86,ARM或MIPS..等).

如下所示,可以先從C與編譯後的BitCode反組譯內容來做為這段落的起點

C Code BitCode反組譯的結果
short FuncBC(int vA)

{

int Y;

Y=((gC+gW+gX)*vA)+40;

Y*=gX+20;

printf("FuncBC:%d\n",Y);

return Y;

}

define signext i16 @FuncBC(i32 %vA) nounwind uwtable {

%1 = alloca i32, align 4

%Y = alloca i32, align 4

store i32 %vA, i32* %1, align 4

%2 = load i8* @gC, align 1

%3 = zext i8 %2 to i64

%4 = load i64* @gW, align 8

%5 = add i64 %3, %4

%6 = load i64* @gX, align 8

%7 = add i64 %5, %6

%8 = load i32* %1, align 4

%9 = sext i32 %8 to i64

%10 = mul i64 %7, %9

%11 = add i64 %10, 40

%12 = trunc i64 %11 to i32

store i32 %12, i32* %Y, align 4

%13 = load i64* @gX, align 8

%14 = add nsw i64 %13, 20

%15 = load i32* %Y, align 4

%16 = sext i32 %15 to i64

%17 = mul nsw i64 %16, %14

%18 = trunc i64 %17 to i32

store i32 %18, i32* %Y, align 4

%19 = load i32* %Y, align 4

%20 = call i32 (i8*, …)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0), i32 %19)

%21 = load i32* %Y, align 4

%22 = trunc i32 %21 to i16

ret i16 %22

}

基於SSA的概念,我們可以把C語言與BitCode程式碼對應如下所示

C Code BitCode反組譯的結果
short FuncBC(int vA) define signext i16 @FuncBC(i32 %vA) nounwind uwtable
int Y; %1 = alloca i32, align 4

//宣告 int Y

%Y = alloca i32, align 4

Y=((gC+gW+gX)*vA)+40 //把 vA儲存到 %1

store i32 %vA, i32* %1, align 4

//把 gC 儲存到 %2,並Extend為i64到%3

%2 = load i8* @gC, align 1

%3 = zext i8 %2 to i64

//把gW儲存到%4,讓%3加%4等於%5

%4 = load i64* @gW, align 8

%5 = add i64 %3, %4

//把gX儲存到 %6

%6 = load i64* @gX, align 8

//讓%6加%5等於%7

%7 = add i64 %5, %6

//讓vA等於%1儲存到%8,並Extend為i64到%9

%8 = load i32* %1, align 4

%9 = sext i32 %8 to i64

//讓%7與%9相乘把結果儲存到%10

%10 = mul i64 %7, %9

//讓%10結果加上40,並儲存到%11

%11 = add i64 %10, 40

//Truncate %11到i32 bits,結果為%12

%12 = trunc i64 %11 to i32

//最後把結果((gC+gW+gX)*vA)+40儲存在 Y

store i32 %12, i32* %Y, align 4

Y*=gX+20; //把 gX放到 %13

%13 = load i64* @gX, align 8

//對 %13 加上20 然後儲存到%14

%14 = add nsw i64 %13, 20

//把 Y值儲存到%15

%15 = load i32* %Y, align 4

//Extend %15到i64後 儲存到%16

%16 = sext i32 %15 to i64

//把%14跟%16相乘後 儲存到%17

%17 = mul nsw i64 %16, %14

//把%17的相乘結果Truncate 後除存在%18

%18 = trunc i64 %17 to i32

//把%18儲存在 Y值

store i32 %18, i32* %Y, align 4

printf("FuncBC:%d\n",Y); //把Y值儲存在%19

%19 = load i32* %Y, align 4

//呼叫外部函式 printf,並把 %19 當做 %d 的參數

%20 = call i32 (i8*, …)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0), i32 %19)

return Y; //把最後Y值結果Truncate 後除存在%22

%21 = load i32* %Y, align 4

%22 = trunc i32 %21 to i16

//以%22作為最後Y值的返回結果

ret i16 %22

原始資料編碼 (Encoding Primitive)

BitStream的封裝會以最少的Bit數來呈現每個有意義的Byte數值,BitStream會把這些原始資料數值以Unsigned Integer 數值的方式編碼,主要的編碼方式包括固定長度整數 (Fixed Width Integer),可變長度整數 (Variable Width Integer),字元編碼(6-bit characters)或32bits方式編碼(Word Alignment)

固定長度整數 (Fixed Width Integer): 例如假設一個8 bits整數,如果要呈現1這個數字,就會以0b00000001的方式來表示. 通常固定長度整數會用來處理習知的數值,最經典的例子就是Boolean 整數,就不會用 32bits來表示,而會以固定長度整數 1bit來代表一個Boolean.

可變長度整數 (Variable Width Integer):可變長度整數,以VBR4來說,就是會以4bits為一組的方式呈現一個VBR欄位,其中最高Bit為0表示該VBR4的4bits組合尚未到結尾,若VBR4最高Bit為1表示該VBR4的數值已經結束.例如: 6 這數字二進位編碼為 0b0110,若用VBR4編碼為0b1110.或像是以8這數字二進位編碼來說為0b1000,若用VBR4編碼則為0b10010000,以每4Bits的VBR4解碼回來看就是把0b1001 最高bit忽略後 << 3 + 0b0000 也就會等於 0b1000. 以’LLVM Bitcode File Format’文件中的例子來說,0x1B ( =27) 來說,原本的二進位呈現方式為0b00011011,以可變長度整數方式來編碼的話,會以每三個bits一組來呈現,以這個例子來說就是 0b011 跟 0b011, 低位址的0b011 由於後面還有0b011要接續在一起,所以他的最高bit 會為0,表示還有接續的3bits內容,而最後的0b011 的最高bit會為1,表示已經到了結尾. 更直接一點來看就是把0x1B=27=0b00011011分拆成 24 + 3 也就是以 0b10110011 的 可變長度整數來呈現,Decode回來的方式就是 0b0011 最高bit為0,表示其後還有數值,目前值為3,而0b1011最高bit為1,表示目前數值已經到結尾,目前值為24 (0b011 << 3),所以 0b10110011解碼後的結果為 0x1B=27.

字元編碼(6-bit characters):6-bit characters encode common characters into a fixed 6-bit field. They represent the following characters with the following 6-bit values:

‘a’ .. ‘z’ —  0 .. 25

‘A’ .. ‘Z’ — 26 .. 51

’0′ .. ’9′ — 52 .. 61

‘.’ — 62

‘_’ — 63

This encoding is only suitable for encoding characters and strings that consist only of the above characters. It is completely incapable of encoding characters not in the set.

32bits方式編碼(Word Alignment):Occasionally, it is useful to emit zero bits until the bitstream is a multiple of 32 bits. This ensures that the bit position in the stream can be represented as a multiple of 32-bit words.

LLVM Bitcode File Format/BitStream Nested Block

有關LLVM Bitcode格式的說明可以參考網頁http://llvm.org/docs/BitCodeFormat.html ,而LLVM也提供一個方便解析LLVM BitCode檔案格式的工具 llvm-bcanalyzer,可用以讓開發者檢視LLVM BitCode檔案格式與對應欄位在編碼後的狀況.

LLVM的BitCode就像是Sun JVM HotSpot或是Google Android Dalvik VM ByteCode的角色一樣,都是提供一個中介的程式編碼IR(Intermediary Representation),再透過可以把這些IR程式碼格式編譯成為優化後的Native機械碼的方式,提供就像是Java JIT一樣可以跨平台但又考慮到不同處理器差異,可藉此提供接近原生程式碼編譯器(例如:GCC)的編譯效能,藉此提供一個高效率的編譯器與編譯後的指令集組合方案.

BitCode總共包含兩個部分,一個是BitStream Container Format,一個是被編碼在Container中的LLVM IR指令集編碼. BitStream Container Format就像是XML的資料結構描述方式,其中包括 Tags與Nested Structures,主要差異在於BitStream Container 為binary方式的編碼儲存,並且支援在這檔案中透過縮寫(Abbreviations)的方式來儲存相關的資料項目名稱,藉此縮小檔案的儲存空間.LLVM IR檔案中會嵌入(embedded ) Wrapper Structure讓LLVM 檔案可以被嵌入額外的資料訊息.

一個標準的BitCode檔案格式,檔案開頭前兩個Bytes會是’ 0×42, 0×43′ (=BC),接下來的兩個Bytes為 Application-Specific Magic Number,以筆者自己所編譯的BitCode檔案來說這兩個值為 ’0xC0,0xDE’,一般的BitCode識別只需要判斷前面兩個Bytes,對特定的應用程式識別來說,則需要判斷完整的四個Bytes.

透過工具llvm-bcanalyzer Dump BitCode檔案時,如果該區塊內容為空,會看到如下的區塊名稱宣告與結尾

<BLOCKINFO_BLOCK/>

若該區塊中有包括相關描述內容,則可看到如下的區塊名稱宣告與結尾

<PARAMATTR_BLOCK NumWords=25 BlockCodeSize=3>

….

</PARAMATTR_BLOCK>

每個區塊的描述都會以 <…> 的括號來區隔,並且對該區塊而言,會以 ‘/’ 作為一個區塊的結束,BitStream中的Block可以包括Nested 巢狀的內容結構,每一個Block都會包括一個依據內容屬性而訂定的特定ID.如下為以 Nested方式呈現的LLVM Block資料內容

<FUNCTION_BLOCK NumWords=5 BlockCodeSize=4> =>最一層的Block,其中包括其它第一層以後的內容

<DECLAREBLOCKS op0=1/> =>第二層的Block,直接以 ‘/’ 收尾

<CONSTANTS_BLOCK NumWords=1 BlockCodeSize=4> =>第二層的Block,其中包括其它第二層以後的內容

<SETTYPE abbrevid=4 op0=17/> =>第三層的Block,直接以 ‘/’ 收尾

<INTEGER abbrevid=5 op0=2/>    =>第三層的Block,直接以 ‘/’ 收尾

</CONSTANTS_BLOCK>

<INST_RET abbrevid=9 op0=48/> =>第二層的Block,直接以 ‘/’ 收尾

</FUNCTION_BLOCK>

由於這些Block的定義在未來是可以根據需求擴充的,也因此在BitCode Format中會把Block 0定義為 Block Information區塊 (BLOCKINFO),用以儲存描述目前BitCode檔案中其他Block相關背景資訊的MetaData.

BitCode檔案格式內的MODULE_BLOCK區塊,是LLVM BitCode檔案格式Nested Block區塊巢狀架構最外層的Block, 檢視BitCoe Block架構與內容最好的方式就是透過指令 ‘ llvm-bcanalyzer -dump ‘,就可以把BitCode檔案格式所包含的Block資訊秀出,如下例子

[root@localhost test]# llvm-bcanalyzer -dump test.bc

<MODULE_BLOCK NumWords=167 BlockCodeSize=3>

<BLOCKINFO_BLOCK/>

<PARAMATTR_BLOCK NumWords=4 BlockCodeSize=3>

<ENTRY op0=4294967295 op1=2199023255584/>

</PARAMATTR_BLOCK>

<TYPE_BLOCK_ID NumWords=15 BlockCodeSize=4>

<NUMENTRY op0=14/>

<INTEGER op0=8/>

<ARRAY abbrevid=9 op0=7 op1=0/>

<POINTER abbrevid=4 op0=1 op1=0/>

<INTEGER op0=32/>

<FUNCTION abbrevid=5 op0=0 op1=0 op2=3/>

<POINTER abbrevid=4 op0=4 op1=0/>

<INTEGER op0=64/>

<FUNCTION abbrevid=5 op0=1 op1=0 op2=6/>

<POINTER abbrevid=4 op0=7 op1=0/>

<POINTER abbrevid=4 op0=0 op1=0/>

<FUNCTION abbrevid=5 op0=1 op1=0 op2=3 op3=9/>

<POINTER abbrevid=4 op0=10 op1=0/>

<POINTER abbrevid=4 op0=3 op1=0/>

<VOID/>

</TYPE_BLOCK_ID>

<TRIPLE op0=120 op1=56 op2=54 op3=95 op4=54 op5=52 op6=45 op7=117 op8=110 op9=107 op10=110 op11=111 op12=119 op13=110 op14=45 op15=108 op16=105 op17=110 op18=117 op19=120 op20=45 op21=103 op22=110 op23=117/>

<DATALAYOUT op0=101 op1=45 op2=112 op3=58 op4=54 op5=52 op6=58 op7=54 op8=52 op9=58 op10=54 op11=52 op12=45 op13=105 op14=49 op15=58 op16=56 op17=58 op18=56 op19=45 op20=105 op21=56 op22=58 op23=56 op24=58 op25=56 op26=45 op27=105 op28=49 op29=54 op30=58 op31=49 op32=54 op33=58 op34=49 op35=54 op36=45 op37=105 op38=51 op39=50 op40=58 op41=51 op42=50 op43=58 op44=51 op45=50 op46=45 op47=105 op48=54 op49=52 op50=58 op51=54 op52=52 op53=58 op54=54 op55=52 op56=45 op57=102 op58=51 op59=50 op60=58 op61=51 op62=50 op63=58 op64=51 op65=50 op66=45 op67=102 op68=54 op69=52 op70=58 op71=54 op72=52 op73=58 op74=54 op75=52 op76=45 op77=118 op78=54 op79=52 op80=58 op81=54 op82=52 op83=58 op84=54 op85=52 op86=45 op87=118 op88=49 op89=50 op90=56 op91=58 op92=49 op93=50 op94=56 op95=58 op96=49 op97=50 op98=56 op99=45 op100=97 op101=48 op102=58 op103=48 op104=58 op105=54 op106=52 op107=45 op108=115 op109=48 op110=58 op111=54 op112=52 op113=58 op114=54 op115=52 op116=45 op117=102 op118=56 op119=48 op120=58 op121=49 op122=50 op123=56 op124=58 op125=49 op126=50 op127=56 op128=45 op129=110 op130=56 op131=58 op132=49 op133=54 op134=58 op135=51 op136=50 op137=58 op138=54 op139=52 op140=45 op141=83 op142=49 op143=50 op144=56/>

<GLOBALVAR op0=2 op1=1 op2=5 op3=9 op4=1 op5=0 op6=0 op7=0 op8=1/>

<FUNCTION op0=5 op1=0 op2=0 op3=0 op4=1 op5=0 op6=0 op7=0 op8=0 op9=0/>

<FUNCTION op0=8 op1=0 op2=1 op3=0 op4=0 op5=0 op6=0 op7=0 op8=0 op9=0/>

<FUNCTION op0=11 op1=0 op2=1 op3=0 op4=0 op5=0 op6=0 op7=0 op8=0 op9=0/>

<CONSTANTS_BLOCK NumWords=6 BlockCodeSize=4>

<SETTYPE abbrevid=4 op0=1/>

<CSTRING abbrevid=10 op0=88 op1=58 op2=37 op3=120 op4=104 op5=10/>

</CONSTANTS_BLOCK>

<FUNCTION_BLOCK NumWords=20 BlockCodeSize=4>

<DECLAREBLOCKS op0=1/>

<CONSTANTS_BLOCK NumWords=4 BlockCodeSize=4>

<SETTYPE abbrevid=4 op0=3/>

<NULL/>

<INTEGER abbrevid=5 op0=2/>

<SETTYPE abbrevid=4 op0=9/>

<CE_INBOUNDS_GEP op0=2 op1=0 op2=3 op3=5 op4=3 op5=5/>

</CONSTANTS_BLOCK>

<INST_ALLOCA op0=12 op1=3 op2=6 op3=3/>

<INST_ALLOCA op0=12 op1=3 op2=6 op3=3/>

<INST_STORE op0=8 op1=5 op2=0 op3=0/>

<INST_CALL op0=0 op1=0 op2=2/>

<INST_CAST abbrevid=7 op0=10 op1=3 op2=0/>

<INST_STORE op0=9 op1=11 op2=3 op3=0/>

<INST_LOAD abbrevid=4 op0=9 op1=3 op2=0/>

<INST_CALL op0=0 op1=0 op2=3 op3=7 op4=12/>

<INST_RET abbrevid=9 op0=5/>

<VALUE_SYMTAB NumWords=1 BlockCodeSize=4>

<ENTRY abbrevid=6 op0=9 op1=88/>

</VALUE_SYMTAB>

</FUNCTION_BLOCK>

<METADATA_BLOCK NumWords=7 BlockCodeSize=3>

<METADATA_KIND op0=0 op1=100 op2=98 op3=103/>

<METADATA_KIND op0=1 op1=116 op2=98 op3=97 op4=97/>

<METADATA_KIND op0=2 op1=112 op2=114 op3=111 op4=102/>

</METADATA_BLOCK>

<VALUE_SYMTAB NumWords=6 BlockCodeSize=4>

<ENTRY abbrevid=6 op0=3 op1=112 op2=114 op3=105 op4=110 op5=116 op6=102/>

<ENTRY abbrevid=6 op0=1 op1=109 op2=97 op3=105 op4=110/>

<ENTRY abbrevid=6 op0=0 op1=46 op2=115 op3=116 op4=114/>

<ENTRY abbrevid=6 op0=2 op1=102 op2=117 op3=110 op4=99 op5=66/>

</VALUE_SYMTAB>

</MODULE_BLOCK>

如下圖所示,為LLVM BitCode檔案格式中不同區塊描述時,Nested Block的示意圖,我們可以看到最外層為MODULE_BLOCK,其下依序包括FUNCTION_BLOCK,METADATA_BLOCK…etc,在Block之中還可以在包括其他的描述Block.

Block ID 0-7預設給BitCode所定義的標準Block區塊.Blokc ID 8 以後為應用程式所特定使用的ID,像是Block ID 12為用以呈現函式實作本體(Function Body)的LLVM IR(Intermediary Representation)內容.

LLVM IR is defined with the following blocks

Block ID 說明
0 BLOCKINFO主要用以儲存描述其它Block區塊資訊的MetaData,根據文件的定義主要包括,SETBID(Code 1,[SETBID (#1), blockid]),用來表示目前描述的資訊是哪個Block ID,在BLOCKINFO中根據所要描述的Block個數,就可以有多筆SETBID宣告. DEFINE_ABBREV([DEFINE_ABBREV, ...])在BLOCKINFO中主要用以表示目前所描述Block Id的縮寫定義.BLOCKNAME (Code 2,[BLOCKNAME, ...name...])是非必要的欄位,用以記錄Block的名稱字串.SETRECORDNAME (Code 3,[SETRECORDNAME, RecordID, ...name...])是非必要欄位,會以第一個參數作為Record ID,其它部分則為這筆Record的名稱字串.

如下為筆者所舉範例的 BLOCKINFO_BLOCK Summary內容,

Block ID #0 ( BLOCKINFO_BLOCK):

Num Instances: 1

Total Size: 637b/79.62B/19W

Percent of file: 11.7096%

Num SubBlocks: 0

Num Abbrevs: 0

Num Records: 0

1 —  7 Block IDs 1-7 保留,用以作為未來的擴充之用.
8 MODULE_BLOCK (=FIRST_APPLICATION_BLOCKID )

這是BitCode檔案格式中最外層的Block,在這Block內會包括整個模組內其它Block的描述內容. 根據目前的定義,MODULE_BLOCK可以包括以下的Sub BLOCK區塊內容.

1,BLOCKINFO

2,PARAMATTR_BLOCK

3,TYPE_BLOCK

4,TYPE_SYMTAB_BLOCK

5,VALUE_SYMTAB_BLOCK

6,CONSTANTS_BLOCK

7,FUNCTION_BLOCK

8,METADATA_BLOCK

如下為筆者所舉範例的 MODULE_BLOCK Summary內容,

Block ID #8 (MODULE_BLOCK):

Num Instances: 1

Total Size: 2544b/318.00B/79W

Percent of file: 46.7647%

Num SubBlocks: 7

Num Abbrevs: 1

Num Records: 6

Percent Abbrevs: 0.0000%

Record Histogram:

Count    # Bits   % Abv  Record Kind

3       225          FUNCTION

1        69          GLOBALVAR

1      1761          DATALAYOUT

1       303          TRIPLE

除了Sub Block外, MODULE_BLOCK主要包括以下資訊內容,

[VERSION, version#] : VERSION (Code 1) 用以表示目前所支援的格式版本

[TRIPLE, ...string...]: TRIPLE (Code 2) 用以儲存Target Triple Specification 字串字元.如下為筆者環境的例子

<TRIPLE op0=120 op1=56 op2=54 op3=95 op4=54 op5=52 op6=45 op7=117 op8=110 op9=107 op10=110 op11=111 op12=119 op13=110 op14=45 op15=108 op16=105 op17=110 op18=117 op19=120 op20=45 op21=103 op22=110 op23=117/>

[DATALAYOUT, ...string...]: DATALAYOUT (Code 3)用以儲存 target datalayout Specification 字串字元.如下為筆者環境的例子

<DATALAYOUT op0=101 op1=45 op2=112 op3=58 op4=54 op5=52 op6=58 op7=54 op8=52 op9=58 op10=54 op11=52 op12=45 op13=105 op14=49 op15=58 op16=56 op17=58 op18=56 op19=45 op20=105 op21=56 op22=58 op23=56 op24=58 op25=56 op26=45 op27=105 op28=49 op29=54 op30=58 op31=49 op32=54 op33=58 op34=49 op35=54 op36=45 op37=105 op38=51 op39=50 op40=58 op41=51 op42=50 op43=58 op44=51 op45=50 op46=45 op47=105 op48=54 op49=52 op50=58 op51=54 op52=52 op53=58 op54=54 op55=52 op56=45 op57=102 op58=51 op59=50 op60=58 op61=51 op62=50 op63=58 op64=51 op65=50 op66=45 op67=102 op68=54 op69=52 op70=58 op71=54 op72=52 op73=58 op74=54 op75=52 op76=45 op77=118 op78=54 op79=52 op80=58 op81=54 op82=52 op83=58 op84=54 op85=52 op86=45 op87=118 op88=49 op89=50 op90=56 op91=58 op92=49 op93=50 op94=56 op95=58 op96=49 op97=50 op98=56 op99=45 op100=97 op101=48 op102=58 op103=48 op104=58 op105=54 op106=52 op107=45 op108=115 op109=48 op110=58 op111=54 op112=52 op113=58 op114=54 op115=52 op116=45 op117=102 op118=56 op119=48 op120=58 op121=49 op122=50 op123=56 op124=58 op125=49 op126=50 op127=56 op128=45 op129=110 op130=56 op131=58 op132=49 op133=54 op134=58 op135=51 op136=50 op137=58 op138=54 op139=52 op140=45 op141=83 op142=49 op143=50 op144=56/>

[ASM, ...string...]: ASM (Code 4)用以儲存個別的BitCode Assembly區塊,不同的Assembly區塊會以0x0A NewLine來區隔開來.

[SECTIONNAME, ...string...]: SECTIONNAME (Code 5)用以儲存不同Section的名稱字串.每一個Section名稱都會對應到一筆 SECTIONNAME資料.

其它包括[DEPLIB, ...string...] (Code 6),[GLOBALVAR, pointer type, isconst, initid, linkage, alignment, section, visibility, threadlocal] (Code 7),[FUNCTION, type, callingconv, isproto, linkage, paramattr, alignment, section, visibility, gc] (Code 8),[ALIAS, alias type, aliasee val#, linkage, visibility] (Code 9),[PURGEVALS, numvals] (Code 10),[GCNAME, ...string...] (Code 11) 都是MODULE_BLOCK中所包括的資訊,筆者在此就不一一說明,有興趣的開發者可以自行參與技術文件.

9 PARAMATTR_BLOCK (Id=9)包含一個用以描述每個Function 參數Parameters屬性的Table.在這表格中的Entry會被FUNCTION區塊的每個Parameters欄位所參考.或是被FUNCTION區塊中的INST_INVOKE與INST_CALL的ATTR欄位所參考.

每筆在PARAMATTR_BLOCK 欄位中的資料,都會是唯一的

PARAMATTR_BLOCK中的資料格式如下

[ENTRY, paramidx0, attr0, paramidx1, attr1...]

筆者舉手中BitCode的 PARAMATTR_BLOCK為例,內容如下所示

<PARAMATTR_BLOCK NumWords=25 BlockCodeSize=3>

<ENTRY op0=4294967295 op1=2199023256096/>

<ENTRY op0=4294967295 op1=2199023255584/>

<ENTRY op0=1 op1=4294967296 op2=4294967295 op3=32/>

<ENTRY op0=4294967295 op1=32/>

<ENTRY op0=1 op1=4294967296 op2=2 op3=4294967296 op4=4294967295 op5=32/>

<ENTRY op0=0 op1=64 op2=4294967295 op3=32/>

<ENTRY op0=2 op1=4294967296 op2=4294967295 op3=32/>

</PARAMATTR_BLOCK>

如下為筆者所舉範例的 PARAMATTR_BLOCK Summary內容,

Block ID #9 (PARAMATTR_BLOCK):

Num Instances: 1

Total Size: 189b/23.62B/5W

Percent of file: 3.4743%

Num SubBlocks: 0

Num Abbrevs: 0

Num Records: 1

Percent Abbrevs: 0.0000%

Record Histogram:

Count    # Bits   % Abv  Record Kind

1       111          ENTRY

10 TYPE_BLOCK (ID=10) 包括了一個在這模組中所使用的Type Table列表,用以表示在這BitCode模組中所參考到的形態.除了NUMENTRY外的資料會產生一個單一型態Type的Entry記錄,包括指令集,常數,MetaData,Type Symbol Table Entry,或其他Type操作單元資料. 每筆在TYPE_BLOCK中的資料都會確保是唯一的.

筆者舉手中BitCode的 TYPE_BLOCK為例,內容如下所示

<TYPE_BLOCK_ID NumWords=35 BlockCodeSize=4>

<NUMENTRY op0=51/>

<INTEGER op0=8/>

<ARRAY abbrevid=9 op0=13 op1=0/>

<POINTER abbrevid=4 op0=1 op1=0/>

……………..

<VOID/>

<INTEGER op0=1/>

<FUNCTION abbrevid=5 op0=0 op1=0 op2=46 op3=18 op4=18 op5=7 op6=17 op7=47/>

<POINTER abbrevid=4 op0=48 op1=0/>

<METADATA/>

</TYPE_BLOCK_ID>

11 CONSTANTS_BLOCK 主要用以儲存在這模組內或所包含的函式所使用到的常數資料,

如下為筆者所舉範例的 CONSTANTS_BLOCK Summary內容,

Block ID #11 (CONSTANTS_BLOCK):

Num Instances: 2

Total Size: 454b/56.75B/14W

Percent of file: 8.3456%

Average Size: 227.00/28.38B/7W

Tot/Avg SubBlocks: 0/0.000000e+00

Tot/Avg Abbrevs: 4/2.000000e+00

Tot/Avg Records: 7/3.500000e+00

Percent Abbrevs: 71.4286%

Record Histogram:

Count    # Bits   % Abv  Record Kind

3        24  100.00  SETTYPE

1        52          CE_INBOUNDS_GEP

1        52  100.00  CSTRING

1        12  100.00  INTEGER

1        16          NULL

12 FUNCTION_BLOCK,主要用以描述Function的本體.

如下為筆者所舉範例的 FUNCTION_BLOCK Summary內容,

Block ID #12 (FUNCTION_BLOCK):

Num Instances: 1

Total Size: 418b/52.25B/13W

Percent of file: 7.6838%

Num SubBlocks: 2

Num Abbrevs: 0

Num Records: 10

Percent Abbrevs: 30.0000%

Record Histogram:

Count    # Bits   % Abv  Record Kind

2        92          INST_CALL

2        80          INST_STORE

2        80          INST_ALLOCA

1        15  100.00  INST_LOAD

1        10  100.00  INST_RET

1        18  100.00  INST_CAST

1        22          DECLAREBLOCKS

13 TYPE_SYMTAB_BLOCK,主要用以描述Type Symbol Table.
14 VALUE_SYMTAB_BLOCK,主要用以描述數值的Symbol Table

如下為筆者所舉範例的 VALUE_SYMTAB_BLOCK Summary內容,

Block ID #14 (VALUE_SYMTAB):

Num Instances: 2

Total Size: 338b/42.25B/10W

Percent of file: 6.2132%

Average Size: 169.00/21.12B/5W

Tot/Avg SubBlocks: 0/0.000000e+00

Tot/Avg Abbrevs: 0/0.000000e+00

Tot/Avg Records: 5/2.500000e+00

Percent Abbrevs: 100.0000%

Record Histogram:

Count    # Bits   % Abv  Record Kind

5       210  100.00  ENTRY

15 METADATA_BLOCK,主要用以描述MetaData項目.

如下為筆者所舉範例的 METADATA_BLOCK Summary內容,

Block ID #15 (METADATA_BLOCK):

Num Instances: 1

Total Size: 285b/35.62B/8W

Percent of file: 5.2390%

Num SubBlocks: 0

Num Abbrevs: 0

Num Records: 3

Percent Abbrevs: 0.0000%

Record Histogram:

Count    # Bits   % Abv  Record Kind

3       195          METADATA_KIND

16 METADATA_ATTACHMENT,主要用以記錄跟函式指令數值有關的MetaData資料.
17 TYPE_BLOCK_ID

如下為筆者所舉範例的 TYPE_BLOCK_ID Summary內容,

Block ID #17 (TYPE_BLOCK_ID):

Num Instances: 1

Total Size: 541b/67.62B/16W

Percent of file: 9.9449%

Num SubBlocks: 0

Num Abbrevs: 6

Num Records: 15

Percent Abbrevs: 66.6667%

Record Histogram:

Count    # Bits   % Abv  Record Kind

6        48  100.00  POINTER

3        49  100.00  FUNCTION

3        78          INTEGER

1        16  100.00  ARRAY

1        16          VOID

1        22          NUMENTRY

基於Nested Block的架構,可以支援有從屬繼承關係的資料屬性或內容,並且可以在Block結構分析時,可以先在其上的Block Id判斷這是否為應該要處理的資料內容,而節省檔案結構分析所需的時間,例如Block ID 3的Nested Block範圍內還有Block ID 13與8,若Block ID 3是目前檔案分析所不需要參考的內容,就可以直接往其後不在Block ID 3內的Block來做分析處理,對系統運作效率上可以得到改善.

LLVM Calling Convention

Calling Convention是每一個語言在處理函式呼叫時,暫存器要如何配置與返回值要如何處理的重要原則,而一個函式呼叫Caller/Callee雙方要能順利執行,也都必須要支持一致的Calling Convention行為,而LLVM基於一個要達成跨平台高執行效率的角色,自然在Call Convention的技術支援上,也該有值得我們深入關注的部份,目前筆者所理解的LLVM主要支援以下的Calling Convention機制,

1,C Calling Convention(CCC):當函式呼叫沒有指定Calling Convention時,預設就會支援這個C Calling Convention模式.會把函式參數由右而左推到Stack中,而被呼叫端則依序透過Stack把函式參數取出.  This calling convention supports varargs function calls and tolerates some mismatch in the declared prototype and implemented declaration of the function (as does normal C).

2,Fast Calling Convention (FastCC):顧名思義,這是一個會盡可能讓呼叫很快速的Calling Convention機制,在函式呼叫過程中所傳遞的函式參數會以處理器暫存器來傳遞,並無需考慮特定的 specified ABI (Application Binary Interface)標準.This calling convention does not support varargs and requires the prototype of all callees to exactly match the prototype of the function definition.

3,Cold Calling Convention: 這是一個讓不是很常被執行的函式,可以在呼叫端有效率的執行呼叫.主要的差異在於在一個函式呼叫的熱區中,有關的參數與數值都會儲存在處理器的暫存器中,如果為了一個非熱區的函式還要把這些暫存器備份到相對速度比較慢的外部記憶體,如此則會對執行效率產生影響.This calling convention does not support varargs and requires the prototype of all callees to exactly match the prototype of the function definition.

4,GHC Convetion (cc10, Glasgow Haskell Compiler): 這是一個由Glasgow Haskell編譯器所支援的Calling Convention.函式參數的傳遞主要透過處理器暫存器進行,並會Disable被呼叫端對處理器暫存器的保存與回復動作,這種呼叫方式主要應用在對執行效能有較高要求的函式呼叫中.如同 Fast Calling Convention一樣, GHC Calling Convention也支持Tail Call的Calling Convention.

在函式呼叫效能的改善上,LLVM也支援Tail Call機制,例如在x86上可讓Caller與Callee在呼叫時共用同樣的Stack Frame,例如FuncA->FuncB->FuncC的呼叫,基於Tail Call機制,可以減少其中透過 call 與重新Push Stack的成本,讓FuncA->FuncB->FuncC可以透過 Jmp與共用 Stack方式,而在FuncC結束時,也可以直接返回到FuncA讓函式呼叫的效率提高,LLVM的Tail Call支援是有平台限制的,且必須Caller與Callee的函式宣告為Fast Calling Convention或是 GHC Convention型態.

結語

法拉第曾說,人心是偏向於錯誤的,人會在自己強烈需要的事情上,欺騙自己.即使尋找印證,也要符合自己的欲望 ,筆者在整理本文時雖盡可能確保資料的正確性,然若有所遺漏也歡迎各位指正.

LLVM要介紹與說明的細節非常多,在這篇文章中筆者只選擇自己感興趣的LLVM技術加以探究,限於篇幅也難以透過一篇文章就把這麼精采的技術項目說明完畢,對LLVM Dig-in越深,也越加覺得這技術在未來發展上的潛力無窮. 隨著Google在未來把PNaCl應用到瀏覽器中,Apple也會把LLVM技術紮根於Mac OS X上,可預見的未來LLVM技術將會更加普及,LLVM不但支援跨平台,還兼顧到執行效率與所耗資源不高的特性,對於現今手機或消費性電子的軟體執行效能改善,將會有相當的助益.

晶片業的獵食者AMD:恢復海盜的心態

晶片業的獵食者AMD:恢復海盜的心態

http://tech.sina.com.cn/it/2012-05-17/15157124345.shtml

福布斯中文網

有人穿著短褲前來,有人穿著T恤前來,人人都帶著問題。去年8月底,超微半導體公司(AMD)在得州奧斯丁園區的全體工作人員在公司禮堂集合。在經歷了挫折連連、痛苦不堪的5年之後,這家晶片製造商的董事會解雇了前任首席執行官,然後花了半年時間尋找接任者。這一天,新任首席執行官羅瑞德(Rory Read)前來向員工們發表演說。對他而言,這絕對是一群難對付的聽眾。
但羅瑞德很快就摸清了聽眾的脾性。儘管得州正是30多度的大熱天,但他仍然西裝革履,髮型一絲不苟,顯得非常精神,看起來就像剛從標著“大公司首席執行官”的盒子裡走出來似的。
AMD公司是從聯想集團(Lenovo)把羅瑞德引誘過來的。聯想集團是AMD公司最大的客戶之一,羅瑞德在那裡工作時就曾觀察到AMD總是搬石頭砸自己的腳,在生產和管理上發生過一系列失誤。AMD曾是英特爾(Intel)最大的競爭對手,當年千方百計想在不斷變化的電腦和移動設備市場站穩腳跟。
據參加過去年那次會議的前AMD員工透露,當時羅瑞德對聽眾們說:“你們艱苦奮鬥才有了今天的成就。AMD人是海盜。我們需要恢復海盜的心態。”
AMD人迫切需要鼓舞人心的領導者,羅瑞德的話正中他們的下懷。當天晚些時候,有人給羅瑞德買了頂海盜帽。一名前AMD員工說:“當時人們就像著了魔似的。”
已經有好長一段時間沒人能這樣形容AMD了。
AMD公司40年的歷史交織著輝煌的技術、錯失的機會、滑稽的行銷噱頭和高風險的法律訴訟。該公司的創始人桑德斯(JerrySanders)是個傳奇人物,曾指導過蘋果公司(Apple)的創始人約伯斯(SteveJobs)。AMD從一家模仿競爭對手的PC晶片製造商轉變成為一家有相當實力的企業,技術出色到能迫使英特爾改變產品設計。
然而最重要的是,AMD公司為了跟上規模更大的競爭對手的步伐而瘋狂努力,結果卻反受其害。不久前,AMD搞砸了一項初步協定——給設備市場最有吸引力的客戶蘋果公司提供處理器。
羅瑞德的工作是確保AMD不會搞砸新的產品線,其中包括替索尼公司(Sony)仍未公佈的新遊戲機秘密研發圖形晶片,讓這個曾經生氣勃勃的公司擺脫數年來的沮喪情緒。
許多投資者已經拋棄了這家公司。過去5年中,AMD股價的跌幅超過了82%,從40.54美元跌到了7.25美元。但是,在將AMD旗下的不斷貶值的晶圓廠作為耗損劃銷掉之後,這家亟待修繕的企業看上去露出了希望的曙光。該公司去年的年收入為66億美元,而現金流就達到5.24億美元。去年11月的裁員中,1400名員工被辭退,此舉可為該公司在2012年省下2億美元。
塞利格曼投資公司(SeligmanInvestments)的投資組合經理佩魯利(SangeethPeruri)購買了AMD流通股的3.56%,他說:“還會跌多少?10%?我認為即使股價漲到20或30美元也不足為奇。”
懷疑論者看到AMD公司正在被三星(微博)電子(Samsung)和高通(微博)公司(Qualcomm)功率強大的智慧手機和平板處理器以及英特爾日益強勁的PC和伺服器處理器左右夾攻。但現年50歲的羅瑞德卻看到該行業正接近“拐點”,這可能會傷害英特爾尖端的處理器設計和先進製造能力的緊密整合。
AMD公司的銷售額僅為英特爾的1/8,企業領導者敢這樣直言不諱,可謂膽大包天。就算桑德斯也從未如此大膽地暗示,英特爾的工廠可能某一天會成為障礙。桑德斯曾說:“好漢就得有晶圓廠。”
但是,在和英特爾在微處理器業務上纏鬥多年,以及後來和英偉達(Nvidia)在圖形晶片業務上多年鏖戰之後,AMD公司如今迎來了一位經歷過PC市場大戰的老將,終於擁有了一個不僅能讓其生存下去、更可能令其茁壯成長的戰略。
故事的最新篇章實際上始自於邁耶(DirkMeyer),這位元工程師設計的Hammer微處理器曾在21世紀初給AMD公司帶來了極大成功。他在桑德斯的繼任者魯毅智(HectorRuiz)手下奮鬥多年後,終於在2008年7月被任命為首席執行官。新官上任後他很快放了幾把火。
首先,他結束了與英特爾曠日持久的反壟斷糾紛,雙方達成和解,AMD獲得了12.5億美元補償。接著,他將AMD的數位電視業務以1.93億美元的價格賣給了博通公司(Broadcom)。然後,他又將移動圖形業務以6,500萬美元的價格賣給了高通公司。
真正的大動作發生在2009年3月,邁耶剝離了AMD資本密集型的晶片工廠,將11億美元債務轉移到名為全球晶圓(GlobalFoundries)的新公司。阿布達比的兩個投資機構在新的合資企業中持有股份,作為交易的一部分,AMD從這些機構處獲得8.25億美元現金。
可惜邁耶挽救AMD的瘋狂努力,並未留給該公司多少追逐新機遇的時間。桑德斯說:“邁耶在挖土逃出深洞。我認為他明白,如果掉在深洞裡,得停止挖掘——假使AMD分心去從事智慧手機等新的活動,那麼洞會變得更深。”
與此同時,新麻煩出現了。2010年11月,微軟(Microsoft)向包括AMD在內的合作夥伴公佈了一個計畫,提供一個新版本的Windows,該版本相容使用英國設計公司安謀國際科技(ARMholdings)的ARM技術的晶片。英偉達、德州儀器(TexasInstruments)和高通公司等生產基於ARM技術的處理器的企業因此得到了機會,可以將產品從移動設備擴展到基於Windows系統的PC和平板電腦。
AMD努力打造自己的無晶圓廠新模式,同時試圖快速研發出將CPU和GPU合二為一的“融聚”(fusion)處理器。從表面上看這個想法是有前途的。代號為“大草原”(Llano)的筆記型電腦處理器引起了蘋果公司的密切關注,該公司原本計畫在2011年年中升級推出新版超輕型MacBookAir。
但是,據一名AMD前雇員說,該公司未能按時將能運行的“大草原”早期樣品提交給蘋果公司。幾位前AMD員工對於當時AMD究竟研發到何種程度持不同的意見。有人說:“我們當時有了早期樣品。”但“大草原”問題太多。AMD最終搞丟了這筆生意。
AMD前雇員說,該公司為蘋果公司的新版電視機上盒推薦名為“布拉索斯”(Brazos)的低成本、低功耗處理器。蘋果沒上鉤,但其他很多廠商對此感興趣。和“大草原”一樣,該處理器在單矽片上整合了CPU和GPU;不同之處在於,AMD可以使用老工藝成本低廉地快速製造。
“布拉索斯”使基於英特爾晶片的廉價上網本的銷量大幅下跌。如今AMD將其描述為史上賣得最快的平臺。一名前雇員說:“如果當初‘布拉索斯’被扼殺了,AMD早就完了。”
在2011年1月,在英偉達宣佈將為摩托羅拉(Motorola)和LG公司的手機提供基於ARM技術的晶片幾天後,AMD公司董事會聯繫了邁耶,不久後邁耶就離職了。
1月20日,AMD董事長克拉夫林(BruceClaflin)在德克薩斯州沃思堡市(FortWorth,Tex。)的凱悅酒店(HyattHotel)向正在休假的AMD經理們宣佈了這個消息。首席財務官塞弗特(ThomasSeifert)擔任臨時首席執行官,但30天內不得作任何決策。有些人在會上哭了。塞弗特說:“邁耶走了,我很難過。我感到其他人也很難過。”
邁耶下臺後第31天,AMD開始檢討公司的戰略。討論結果是決定不製造智慧手機晶片,因為那耗費的時間和金錢超過公司的能力。AMD的董事會最終決定求助於羅瑞德——這位曾在IBM(微博)公司工作過23年的資深人士在2006年6月被任命為聯想集團總裁,自那時之後,聯想集團在全球PC市場的份額,從2009年初的6.6%提高到了2011年第2季度的12.1%。
對曾與羅瑞德共事的人來說,他最出名的是能嫺熟地與數位打交道,以及用逗樂的天賦激勵同事。他在IBM公司時曾組織在晚上乘碰碰車打籃球,以此犒勞團隊,在開車搶球的過程中他開懷大笑。
在聯想集團時,為了讓說不同語言的全球員工明白自己的商業策略,他像拳擊手一樣舉起雙拳,以此闡明集團的戰略:一隻拳頭攻擊新市場,另一隻保護現有業務。前聯想集團首席執行官阿梅裡奧(WilliamAmelio)說:“他能用令人信服的方式闡述明天會比今天更好,成功就像擺在我們面前的一條上坡道。”
AMD在PC和伺服器市場與英特爾對抗期間,曾錯過一些機會,羅瑞德正在重新尋找這些機會。去年10月,他聘請了新的首席技術官——曾在蘋果公司和IBM公司工作的業界老將佩珀馬斯特(MarkPapermaster)。佩珀馬斯特採取的第一項措施,是讓AMD在設計CPU和GPU時,某些元件可以更容易地改用其他公司設計的技術。佩珀馬斯稱之為“左右逢迎的計算”。
這個信號明確地顯示,AMD正在探索將x86處理器內核改為ARM等技術的想法。這也開闢了製造更多“嵌入式”處理器的機會,譬如用於汽車和醫療設備的晶片,在這些領域裡,AMD可以調整自己的產品線,出擊英特爾不屑一顧的利基市場。
羅瑞德將AMD的遊戲機晶片專案作為榜樣。微軟在Xbox遊戲機內使用了AMD的圖形技術。另一個與索尼公司合作的尚未公佈的專案也正在進行中,可能會取代曾為PS3遊戲機提供晶片的英偉達。索尼公司拒絕對此發表評論。
最激進的變革可能是:羅瑞德說,AMD可以為客戶定制元件,從而鎖定供應商。前AMD雇員說,以往AMD與英特爾競爭,為整個PC行業提供晶片時,可不願採用定制的做法。
然而,短期內重要的是羅瑞德別把事情搞砸。原先雄心勃勃地列入2012年計畫的10核和20核伺服器晶片已被取消,取而代之的是將在今年晚些時候推出的低功耗處理器“本州”(Hondo)。AMD得讓自己能更容易地兌現諾言。
如果說這是個縮水的戰略,那麼它是AMD有足夠資源實現的,畢竟一系列新湧現出來的對手在威脅著AMD和英特爾。三星電子和安謀國際科技已經在奧斯丁建立了設計中心並挖角AMD員工,為進軍由英特爾統治的伺服器處理器市場做準備。
羅瑞德尚未將注意力從英特爾身上移開。羅瑞德說,當年IBM公司憑藉對大型機業務的壟斷所獲得的利潤,使其可以進行其他公司無力進行的投資;同樣,英特爾對PC處理器市場的控制,能讓該公司支付得起其他公司無法負擔的工廠。
羅瑞德說:“利潤曾經可以負擔得起整個成本結構?但那一切已成為過去。我們不能基於一個可能會改變的商業模式,被牽著鼻子繞著一項資產基礎走。”
未來幾年處理器市場的走勢並不明朗。羅瑞德說:“我們必須向前看,得成為獵食者。我們得掃視大草原,尋找機會。”有蘋果公司、三星電子、高通公司、英偉達和德州儀器在市場上,AMD決不能只把眼睛盯在英特爾身上。

[笑話] 給程式設計師的笑話

http://disp.cc/b/115-3xkZ

—1
"想要成為程式設計師,應該從哪一種語言開始學起好呢?"

"英語。"

—2
"爸爸,為什麼大象的鼻子這麼長呢?"

"那是規格啊。"

—3
一天有二十四小時,但是所謂的"今天之內",指的是到明天早上上班為止。

—4
寫一個程式有兩種方式:

第一種是把程式碼寫的很簡單、很明顯不容易有缺陷。

第二種是把程式碼寫得很複雜,讓人看不出有明顯的缺陷。

—5
對客戶來說只有兩種東西不用錢:白開水、還有追加規格。

—6
世界上可以分成10種人,懂二進位的人,以及不懂二進位的人。

—7
對著一個程式大發脾氣:"是誰寫了這麼奇怪的程式碼!"

旁人:"那是你去年寫的喔。"

—8
"你好,我是全型空白,我現在在你的程式碼裡面。"
"你好,我是\,我現在在你程式碼的註解裡面。"
"你好,我是-,我也不曉得為什麼我會在這個參數前面"
"你好,我是=,我現在在你程式碼的if判斷式裡面。"(推文哽 XD)

—9
老婆:"下班後買十個包子回來,如果看到賣西瓜的就買一個。"
老公:"好。" (下班後帶著一個包子回家)
老婆:"為什麼只有一個包子?"
老公:"因為我看到賣西瓜的。"

—10
女孩:"我警告你,你再不停手我就要叫警察了喔。"
程式設計師:"警告不重要啦,不是錯誤就好。"

—11
"為什麼程式設計師常常不會分萬聖節跟聖誕節?"
"喔,因為OCT 31 = DEC 25啊。"

—12
程式異常在期限還有一個月的時候稱做Bug,在期限還剩三天的時候則稱為規格限制

—13
不懂電腦的操作者發現Bug的頻率經常比QA工程師還高,

缺點是他們通常沒有辦法重現這個Bug。

Oracle v. Google – What’s the Deal With the Java Specification License?

http://www.groklaw.net/article.php?story=2012041812230622

The opening statements having been made, and it is worth a look at some of the slides presented by each of the parties regarding the use of the Java API specification (i.e., the narrative telling you what needs to be in an API implementation) and the claims of each party. There is a clear disconnect between what Oracle claims and the practices that Sun and now Oracle follow when it comes to the Java API specifications, and it shows up graphically when one looks at the following slides and then looks at the actual Java API specification license. Let’s start by looking at the Oracle slides.
On Oracle slide 33 Oracle asserts that one must have a license to "provide class libraries based on Java API designs." Of course, this is already a bit ambiguous because they don’t say what kind of license you actually need, just that you need to have some form of license. You will also note that they love to now use the term "designs" instead of "specifications." That clearly is intended to boost their asserting that there is creative expression protected by copyright in those specifications.