ARM NEON 編程系列2 - 基本指令集

轉(zhuǎn)載自:http://www.cnblogs.com/xylc/p/5410517.html

方便筆記和后續(xù)學習哪亿,如有侵權(quán)請聯(lián)系刪除

前言

本系列博文用于介紹ARM CPU下NEON指令優(yōu)化。

博文github地址:github

相關(guān)代碼github地址:github

NEON指令集

主流支持目標平臺為ARM CPU的編譯器基本都支持NEON指令睬隶÷嗉校可以通過在代碼中嵌入NEON匯編來使用NEON,但是更加常見的方式是通過類似C函數(shù)的NEON Instrinsic來編寫NEON代碼苏潜。就如同NEON hello world一樣银萍。NEON Instrinsic是編譯器支持的一種buildin類型和函數(shù)的集合,基本涵蓋NEON的所有指令恤左,通常這些Instrinsic包含在arm_neon.h頭文件中贴唇。

本文以android-ndk-r11c中armv7的arm_neon.h為例,講解NEON的指令類型飞袋。

寄存器

ARMV7架構(gòu)包含:

16個通用寄存器(32bit)戳气,R0-R15

16個NEON寄存器(128bit),Q0-Q15(同時也可以被視為32個64bit的寄存器巧鸭,D0-D31)

16個VFP寄存器(32bit)瓶您,S0-S15

NEON和VFP的區(qū)別在于VFP是加速浮點計算的硬件不具備數(shù)據(jù)并行能力,同時VFP更盡興雙精度浮點數(shù)(double)的計算纲仍,NEON只有單精度浮點計算能力呀袱。更多請參考stackoverflow:neon vs vfp

基本數(shù)據(jù)類型

64bit數(shù)據(jù)類型,映射至寄存器即為D0-D31

相應的c/c++語言類型(stdint.h或者csdtint頭文件中類型)在注釋中說明郑叠。

//typedef int8_t[8] int8x8_t;typedef__builtin_neon_qiint8x8_t__attribute__((__vector_size__ (8)));//typedef int16_t[4] int16x4_t;typedef__builtin_neon_hiint16x4_t__attribute__((__vector_size__ (8)));//typedef int32_t[2] int32x2_t;typedef__builtin_neon_siint32x2_t__attribute__((__vector_size__ (8)));//typedef int64_t[1] int64x1_t;typedef__builtin_neon_diint64x1_t;//typedef float16_t[4] float16x4_t;//(注:該類型為半精度夜赵,在部分新的CPU上支持,c/c++語言標注中尚無此基本數(shù)據(jù)類型)typedef__builtin_neon_hffloat16x4_t__attribute__((__vector_size__ (8)));//typedef float32_t[2] float32x2_t;typedef__builtin_neon_sffloat32x2_t__attribute__((__vector_size__ (8)));//poly8以及poly16類型在常用算法中基本不會使用//詳細解釋見://http://stackoverflow.com/questions/22224282/arm-neon-and-poly8-t-and-poly16-ttypedef__builtin_neon_poly8poly8x8_t__attribute__((__vector_size__ (8)));typedef__builtin_neon_poly16poly16x4_t__attribute__((__vector_size__ (8)));#ifdef__ARM_FEATURE_CRYPTOtypedef__builtin_neon_poly64poly64x1_t;#endif//typedef uint8_t[8] uint8x8_t;typedef__builtin_neon_uqiuint8x8_t__attribute__((__vector_size__ (8)));//typedef uint16_t[4] uint16x4_t;typedef__builtin_neon_uhiuint16x4_t__attribute__((__vector_size__ (8)));//typedef uint32_t[2] uint32x2_t;typedef__builtin_neon_usiuint32x2_t__attribute__((__vector_size__ (8)));//typedef uint64_t[1] uint64x1_t;typedef__builtin_neon_udiuint64x1_t;

128bit數(shù)據(jù)類型乡革,映射至寄存器即為Q0-Q15

相應的c/c++語言類型(stdint.h或者csdtint頭文件中類型)在注釋中說明寇僧。

//typedef int8_t[16] int8x16_t;typedef__builtin_neon_qiint8x16_t__attribute__((__vector_size__ (16)));//typedef int16_t[8] int16x8_t;typedef__builtin_neon_hiint16x8_t__attribute__((__vector_size__ (16)));//typedef int32_t[4] int32x4_t;typedef__builtin_neon_siint32x4_t__attribute__((__vector_size__ (16)));//typedef int64_t[2] int64x2_t;typedef__builtin_neon_diint64x2_t__attribute__((__vector_size__ (16)));//typedef float32_t[4] float32x4_t;typedef__builtin_neon_sffloat32x4_t__attribute__((__vector_size__ (16)));//poly8以及poly16類型在常用算法中基本不會使用//詳細解釋見://http://stackoverflow.com/questions/22224282/arm-neon-and-poly8-t-and-poly16-ttypedef__builtin_neon_poly8poly8x16_t__attribute__((__vector_size__ (16)));typedef__builtin_neon_poly16poly16x8_t__attribute__((__vector_size__ (16)));#ifdef__ARM_FEATURE_CRYPTOtypedef__builtin_neon_poly64poly64x2_t__attribute__((__vector_size__ (16)));#endif//typedef uint8_t[16] uint8x16_t;typedef__builtin_neon_uqiuint8x16_t__attribute__((__vector_size__ (16)));//typedef uint16_t[8] uint16x8_t;typedef__builtin_neon_uhiuint16x8_t__attribute__((__vector_size__ (16)));//typedef uint32_t[4] uint32x4_t;typedef__builtin_neon_usiuint32x4_t__attribute__((__vector_size__ (16)));//typedef uint64_t[2] uint64x2_t;typedef__builtin_neon_udiuint64x2_t__attribute__((__vector_size__ (16)));typedeffloatfloat32_t;typedef__builtin_neon_poly8poly8_t;typedef__builtin_neon_poly16poly16_t;#ifdef__ARM_FEATURE_CRYPTOtypedef__builtin_neon_poly64poly64_t;typedef__builtin_neon_poly128poly128_t;#endif

結(jié)構(gòu)化數(shù)據(jù)類型

下面這些數(shù)據(jù)類型是上述基本數(shù)據(jù)類型的組合而成的結(jié)構(gòu)化數(shù)據(jù)類型,通常為被映射到多個寄存器中沸版。

typedefstructint8x8x2_t{int8x8_tval[2];}int8x8x2_t;...//省略......#ifdef__ARM_FEATURE_CRYPTOtypedefstructpoly64x2x4_t{poly64x2_tval[4];}poly64x2x4_t;#endif

基本指令集

NEON指令按照操作數(shù)類型可以分為正常指令嘁傀、寬指令、窄指令视粮、飽和指令细办、長指令。

正常指令:生成大小相同且類型通常與操作數(shù)向量相同到結(jié)果向量馒铃。

長指令:對雙字向量操作數(shù)執(zhí)行運算蟹腾,生產(chǎn)四字向量到結(jié)果。所生成的元素一般是操作數(shù)元素寬度到兩倍区宇,并屬于同一類型娃殖。L標記,如VMOVL议谷。

寬指令:一個雙字向量操作數(shù)和一個四字向量操作數(shù)執(zhí)行運算炉爆,生成四字向量結(jié)果。W標記卧晓,如VADDW芬首。

窄指令:四字向量操作數(shù)執(zhí)行運算,并生成雙字向量結(jié)果逼裆,所生成的元素一般是操作數(shù)元素寬度的一半郁稍。N標記,如VMOVN胜宇。

飽和指令:當超過數(shù)據(jù)類型指定到范圍則自動限制在該范圍內(nèi)耀怜。Q標記,如VQSHRUN

NEON指令按照作用可以分為:加載數(shù)據(jù)桐愉、存儲數(shù)據(jù)财破、加減乘除運算、邏輯AND/OR/XOR運算从诲、比較大小運算等左痢,具體信息參考資料[1]中附錄C和附錄D部分。

常用的指令集包括:

初始化寄存器

寄存器的每個lane(通道)都賦值為一個值N

Result_tvcreate_type(Scalar_t N)Result_tvdup_type(Scalar_t N)Result_tvmov_type(Scalar_t N)

lane(通道)在下面有說明系洛。

加載內(nèi)存數(shù)據(jù)進寄存器

間隔為x俊性,加載數(shù)據(jù)進NEON寄存器

Result_t vld[x]_type(Scalar_t* N)

Result_t vld[x]q_type(Scalar_t* N)

間隔為x,加載數(shù)據(jù)進NEON寄存器的相關(guān)lane(通道)碎罚,其他lane(通道)的數(shù)據(jù)不改變

Result_t vld[x]_lane_type(Scalar_t* N,Vector_t M,intn)Result_t vld[x]q_lane_type(Scalar_t* N,Vector_t M,intn)

從N中加載x條數(shù)據(jù)磅废,分別duplicate(復制)數(shù)據(jù)到寄存器0-(x-1)的所有通道

Result_t vld[x]_dup_type(Scalar_t* N)

Result_t vld[x]q_dup_type(Scalar_t* N)

lane(通道):比如一個float32x4_t的NEON寄存器,它具有4個lane(通道)荆烈,每個lane(通道)有一個float32的值拯勉,因此c++ float32x4_t dst = vld1q_lane_f32(float32_t* ptr,float32x4_t src,int n=2)的意思就是先將src寄存器的值復制到dst寄存器中,然后從ptr這個內(nèi)存地址中加載第3個(lane的index從0開始)float到dst寄存器的第3個lane(通道中)憔购。最后dst的值為:{src[0],src[1],ptr[2],src[3]}宫峦。

間隔:交叉存取,是ARM NEON特有的指令玫鸟,比如c++ float32x4x3_t = vld3q_f32(float32_t* ptr)导绷,此處間隔為3,即交叉讀取12個float32進3個NEON寄存器中屎飘。3個寄存器的值分別為:{ptr[0],ptr[3],ptr[6],ptr[9]}妥曲,{ptr[1],ptr[4],ptr[7],ptr[10]}贾费,{ptr[2],ptr[5],ptr[8],ptr[11]}。

存儲寄存器數(shù)據(jù)到內(nèi)存

間隔為x檐盟,存儲NEON寄存器的數(shù)據(jù)到內(nèi)存中

voidvstx_type(Scalar_t* N)voidvstxq_type(Scalar_t* N)

間隔為x褂萧,存儲NEON寄存器的相關(guān)lane(通道)到內(nèi)存中

Result_t vst[x]_lane_type(Scalar_t* N,Vector_t M,intn)Result_t vst[x]q_lane_type(Scalar_t* N,Vector_t M,intn)

讀取/修改寄存器數(shù)據(jù)

讀取寄存器第n個通道的數(shù)據(jù)

Result_tvget_lane_type(Vector_t M,intn)

讀取寄存器的高/低部分到新的寄存器中,數(shù)據(jù)變窄(長度減半)葵萎。

Result_tvget_low_type(Vector_t M)Result_tvget_high_type(Vector_t M)

返回在復制M的基礎上設置通道n為N的寄存器數(shù)據(jù)

Result_tvset_lane_type(Scalar N,Vector_t M,intn)

寄存器數(shù)據(jù)重排

從寄存器M中取出后n個通道的數(shù)據(jù)置于低位导犹,再從寄存器N中取出x-n個通道的數(shù)據(jù)置于高位,組成一個新的寄存器數(shù)據(jù)羡忘。

Result_tvext_type(Vector_t N,Vector_t M,intn)Result_tvextq_type(Vector_t N,Vector_t M,intn)

其他數(shù)據(jù)重排指令還有:

vtbl_tyoe,vrev_type,vtrn_type,vzip_type,vunzip_type,vcombine ...

等以后有時間一一講解谎痢。

類型轉(zhuǎn)換指令

強制重新解釋寄存器的值類型,從SrcType轉(zhuǎn)化為DstType卷雕,其內(nèi)部實際值不變且總的字節(jié)數(shù)不變节猿,舉例:vreinterpret_f32_s32(int32x2_t),從int32x2_t轉(zhuǎn)化為float32x2_t爽蝴。

vreinterpret_DstType_SrcType(Vector_t N)

算數(shù)運算指令

[普通指令] 普通加法運算 res = M+N

Result_tvadd_type(Vector_t M,Vector_t N)Result_tvaddq_type(Vector_t M,Vector_t N)

[長指令] 變長加法運算 res = M+N沐批,為了防止溢出,一種做法是使用如下指令蝎亚,加法結(jié)果存儲到長度x2的寄存器中九孩,如:vuint16x8_t res = vaddl_u8(uint8x8_t M,uint8x8_t N)。

Result_tvaddl_type(Vector_t M,Vector_t N)

[寬指令] 加法運算 res = M+N发框,第一個參數(shù)M寬度大于第二個參數(shù)N躺彬。

Result_tvaddw_type(Vector_t M,Vector_t N)

[普通指令] 加法運算 res = trunct(M+N)(溢出則截斷)之后向右平移1位,即計算M和N的平均值

Result_tvhadd_type(Vector_t M,Vector_t N)

[普通指令] 加法運算 res = round(M+N)(溢出則循環(huán))之后向右平移1位梅惯,即計算M和N的平均值

Result_tvrhadd_type(Vector_t M,Vector_t N)

[飽和指令] 飽和加法運算 res = st(M+N)宪拥,如:vuint8x8_t res = vqadd_u8(uint8x8_t M,uint8x8_t N),res超出int8_t的表示范圍(0铣减,255)她君,比如256,則設為255.

Result_tvqadd_type(Vector_t M,Vector_t N)

[窄指令] 加法運算 res = M+N葫哗,結(jié)果比參數(shù)M/N的長度小一半缔刹,如 uint8x8_t res = vaddhn_u16(uint16x8_t M,uint16x8_t N)

Result_tvaddhn_type(Vector_t M,Vector_t N)

[普通指令] 減法運算 res = M-N

Result_tvsub_type(Vector_t M,Vector_t N)

[普通指令] 乘法運算 res = M*N

Result_tvmul_type(Vector_t M,Vector_t N)Result_tvmulq_type(Vector_t M,Vector_t N)

[普通指令] 乘&加法運算 res = M+N*P

Result_tvmla_type(Vector_t M,Vector_t N,Vector_t P)Result_tvmlaq_type(Vector_t M,Vector_t N,Vector_t P)

[普通指令] 乘&減法運算 res = M-N*P

Result_tvmls_type(Vector_t M,Vector_t N,Vector_t P)Result_tvmlsq_type(Vector_t M,Vector_t N,Vector_t P)

類似加法運算,減法和乘法運算也有一系列變種...

數(shù)據(jù)處理指令

[普通指令] 計算絕對值 res=abs(M)

Result_tvabs_type(Vector_t M)

[普通指令] 計算負值 res=-M

Result_tvneg_type(Vector_t M)

[普通指令] 計算最大值 res=max(M,N)

Result_tvmax_type(Vector_t M,Vector_t N)

[普通指令] 計算最小值 res=min(M,N)

Result_tvmin_type(Vector_t M,Vector_t N)

...

比較指令

[普通指令] 比較是否相等 res=mask(M == N)

Result_tvceg_type(Vector_t M,Vector_t N)

[普通指令] 比較是否大于或等于 res=mask(M >= N)

Result_tvcge_type(Vector_t M,Vector_t N)

[普通指令] 比較是否大于 res=mask(M > N)

Result_tvcgt_type(Vector_t M,Vector_t N)

[普通指令] 比較是否小于或等于 res=mask(M <= N)

Result_tvcle_type(Vector_t M,Vector_t N)

[普通指令] 比較是否小于 res=mask(M < N)

Result_tvclt_type(Vector_t M,Vector_t N)

...

歸約指令

[普通指令] 歸約加法劣针,M和N內(nèi)部的元素各自相加校镐,最后組成一個新的結(jié)果

Result_tvpadd_type(Vector_t M,Vector_t N)

[普通指令] 歸約最大比較,M和N內(nèi)部的元素比較得出最大值捺典,最后組成一個新的結(jié)果

Result_tvpmax_type(Vector_t M,Vector_t N)

[普通指令] 歸約最小比較鸟廓,M和N內(nèi)部的元素比較得出最小值,最后組成一個新的結(jié)果

Result_tvpmin_type(Vector_t M,Vector_t N)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市引谜,隨后出現(xiàn)的幾起案子牍陌,更是在濱河造成了極大的恐慌,老刑警劉巖员咽,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呐赡,死亡現(xiàn)場離奇詭異,居然都是意外死亡骏融,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門萌狂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來档玻,“玉大人,你說我怎么就攤上這事茫藏∥笈浚” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵务傲,是天一觀的道長凉当。 經(jīng)常有香客問我,道長售葡,這世上最難降的妖魔是什么看杭? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮挟伙,結(jié)果婚禮上楼雹,老公的妹妹穿的比我還像新娘。我一直安慰自己尖阔,他們只是感情好贮缅,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布碘赖。 她就那樣靜靜地躺著懈词,像睡著了一般。 火紅的嫁衣襯著肌膚如雪佣渴。 梳的紋絲不亂的頭發(fā)上齿坷,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天桂肌,我揣著相機與錄音,去河邊找鬼胃夏。 笑死轴或,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的仰禀。 我是一名探鬼主播照雁,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饺蚊?” 一聲冷哼從身側(cè)響起萍诱,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎污呼,沒想到半個月后裕坊,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡燕酷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年籍凝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苗缩。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡饵蒂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酱讶,到底是詐尸還是另有隱情退盯,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布泻肯,位于F島的核電站渊迁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏灶挟。R本人自食惡果不足惜琉朽,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稚铣。 院中可真熱鬧漓骚,春花似錦、人聲如沸榛泛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽曹锨。三九已至孤个,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沛简,已是汗流浹背齐鲤。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留椒楣,地道東北人给郊。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像捧灰,于是被迫代替她去往敵國和親淆九。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容