C_and_CPP 板


LINE

本版首Po, 查了一下好像没有相关讨论, 请大大们鞭小力一点QQ 网志好读版: http://tinyurl.com/y677eu2f 最近从同事那里听到这个小技巧, 分享给大家 Tip: 建议尽量使用structure来存取Register,可以获得以下好处 1. 让compiler对base address计算做最佳化 (with -O1),让程式更有效率 2. 易写、易读、易懂! 正文开始! 让compiler对base address计算做最佳化 (with -O1),让程式更有效率 基本概念: Placing C variables at specific addresses to access memory-mapped peripherals The ARM compiler will normally use a ‘base register’ plus the immediate offset field available in the load/store instruction to compile struct member or specific array element access. In the ARM instruction set, LDR/STR word/byte instructions have a 4KB range, but LDRH/STRH instructions have a smaller immediate offset of 256 bytes. Equivalent 16-bit Thumb instructions are much more restricted - LDR/STR have a range of 32 words, LDRH/STRH have a range of 32 halfwords and LDRB/STRB have a range of 32 bytes. However, 32-bit Thumb instructions offer a significant improvement. Hence, it is important to group related peripheral registers near to each other if possible. The compiler will generally do a good job of minimising the number of instructions required to access the array elements or structure members by using base registers. 以上大意上是说ARM compiler原本就会使用base register加上offset来 对struct member与array element来做存取,所以如果我们将一组连续位置的register用 struct或array来定义,就可以也套用上述的base register存取方式。 直接看例子比较快,如果我们直接用下面这样的方法去写A/B/C #define REG_BASE_ADDR (0x10000000FFFFF00) #define REG_A (REG_BASE_ADDR + 0x8) #define REG_B (REG_BASE_ADDR + 0x10) #define REG_C (REG_BASE_ADDR + 0x18) #define READ_REG(reg, val) val = *((volatile unsigned long *) (reg)) #define WRITE_REG(reg, val) *((volatile unsigned long *) (reg)) = val void foo(unsigned long a_val, unsigned long b_val, unsigned long c_val){ WRITE_REG(REG_A, a_val); WRITE_REG(REG_B, b_val); WRITE_REG(REG_C, c_val); } 从Compiler Explorer(ARM64 GCC 8.2 -O2)测试的assembly结果如下 (https://godbolt.org/z/3MRiMJ) 可以看到需要分别计算A/B/C register的base address(Line2~10)才能写值。 foo: mov x5, 65288 mov x4, 65296 movk x5, 0xfff, lsl 16 movk x4, 0xfff, lsl 16 movk x5, 0x100, lsl 48 mov x3, 65304 movk x4, 0x100, lsl 48 movk x3, 0xfff, lsl 16 movk x3, 0x100, lsl 48 str x0, [x5] str x1, [x4] str x2, [x3] ret 而如果改成下面的写法,利用structure来存取 (https://godbolt.org/z/g-eJmz) #define REG_BASE_ADDR (0x10000000FFFFF00) typedef struct { unsigned long BASE; unsigned long REG_A; unsigned long REG_B; unsigned long REG_C; } my_register; #define READ_REG(reg, val) do{ \ volatile my_register *base = (my_register *) REG_BASE_ADDR; \ val = base->reg; \ } while(0) #define WRITE_REG(reg, val) do{ \ volatile my_register *base = (my_register *) REG_BASE_ADDR; \ base->reg = val; \ } while(0) void foo(unsigned long a_val, unsigned long b_val, unsigned long c_val){ WRITE_REG(REG_A, a_val); WRITE_REG(REG_B, b_val); WRITE_REG(REG_C, c_val); } 产生的Assembly如下,可以看到只需要去计算Base Address一次,并存在base register x3,接着直接透过offset去读A/B/C,整整少了一半的指令数!对於斤斤计较MCPS的Hard Real Time Context来说可是有天壤之别! foo: mov x3, 268435200 movk x3, 0x100, lsl 48 str x0, [x3, 8] str x1, [x3, 16] str x2, [x3, 24] ret 易写、易读、易懂! 再来看第二个优点,这点对我来说跟甚至比程式效率还重要,而这其实也是Structure本 身最大的用途:用对工程师最友善的方式来描述资料 例如Register A的Spec如下 (little-endian) Bit | 0 1 2 3 4 5 6 7 8------15-------------- 63 | Field | X | Y | Z | W | 假如要对Y写值(故意挑个中间的),以 bit operation操作的话会写成如下,我想看到 SET_REG_A_Y_FIELD就知道我的意思了吧! 实在是有够麻烦,当Regitser / Field一多根 本没办法维护。 #define REG_BASE_ADDR (0x10000000FFFFF00) #define REG_A (REG_BASE_ADDR + 0x8) #define REG_B (REG_BASE_ADDR + 0x10) #define REG_C (REG_BASE_ADDR + 0x18) #define READ_REG(reg, val) val = *((volatile unsigned long *) (reg)) #define WRITE_REG(reg, val) *((volatile unsigned long *) (reg)) = val #define SET_REG_A_Y_FIELD(val) do { \ unsigned long cur_val = READ_REG(REG_A, cur_val); \ cur_val = (cur_val & ~0xF0) | ( (val & 0xF) << 0x4); \ WRITE_REG(REG_A, cur_val); \ } while (0) void foo(unsigned char n){ SET_REG_A_Y_FIELD(n); } Compile Result (ARM64 GCC 8.2 -O2) https://godbolt.org/z/kftmDw foo: mov x2, 65288 ubfiz x0, x0, 4, 4 movk x2, 0xfff, lsl 16 movk x2, 0x100, lsl 48 ldr x1, [x2] and x1, x1, -241 orr x0, x0, x1 str x0, [x2] ret 而Structure的写法如下,有没有非常简单!? 甚至,能够指定任意的Field,而且指令数 还更少!! 因为ARM(MIPS /x86也有)有支援直接对Register某一群bit读/写值,下面的bfi 就是,这样就可以省掉AND与OR操作了。 (Reference: 3.8.1. BFC and BFI) BFI copies a bitfield into one register from another register. It replaces width bits in Rd starting at the low bit position lsb, with width bits from Rn starting at bit[0]. Other bits in Rd are unchanged. #define REG_BASE_ADDR (0x10000000FFFFF00) typedef struct { unsigned long X:4; unsigned long Y:4; unsigned long Z:8; unsigned long W:48; } reg_a_t; typedef struct { unsigned long BASE; reg_a_t REG_A; unsigned long REG_B; unsigned long REG_C; } my_register; #define READ_REG(reg, val) do{ \ volatile my_register *base = (my_register *) REG_BASE_ADDR; \ val = base->reg; \ } while(0) #define WRITE_REG(reg, val) do{ \ volatile my_register *base = (my_register *) REG_BASE_ADDR; \ base->reg = val; \ } while(0) #define SET_REG_A_FIELD(field, val) do { \ WRITE_REG(REG_A.field, val); \ } while (0) void foo(unsigned char n){ SET_REG_A_FIELD(Y,n); } Compile Result (ARM64 GCC 8.2 -O2) https://godbolt.org/z/eIyDNY foo: mov x1, 268435200 movk x1, 0x100, lsl 48 ldr x2, [x1, 8] bfi x2, x0, 4, 4 str x2, [x1, 8] ret 顺带一提,其实近年来compiler的优化已经越做越极致了,在想自干assembly前先去翻翻 GCC Optimization Option还有用Compiler Explorer玩一玩再决定吧!! --



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 36.229.45.83
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1552658080.A.27D.html ※ 编辑: hsnuer1171 (36.229.45.83), 03/15/2019 21:55:57
1F:推 TitanEric: 优文 前阵子翻bios的code也有看到类似的trick 03/16 00:02
2F:→ chuegou: 虽然知道这件事 但第一次看到分析 推个 03/16 01:15
3F:推 descent: 感谢分享 03/16 18:32
4F:推 ggBird: 推,我写 driver 也会用 03/16 22:11
5F:推 KALR: 如果是driver的跨平台层,其实不建议使用bit field 03/17 22:18
6F:→ KALR: 因为bit field 是 implementation-defined 03/17 22:19
7F:→ KALR: 同一份code在Linux, window, Mac compile有可能会得到不同 03/17 22:20
8F:→ KALR: 的结果 03/17 22:20
9F:推 cole945: 不建议bitfield+1,不过其实他会被ABI规范.x86-32没统一 03/18 22:40
10F:→ cole945: msvc自成一套. 通常看endian会决定顺序,又分能不能跨unit 03/18 22:40
11F:→ cole945: arm32可以跨,跟大家比较不一样. 03/18 22:41
12F:→ cole945: GCC的instruction select很强,不需要bitfield就可以match 03/18 22:42
13F:→ cole945: 出bitfield extrac/insert的行为. 03/18 22:42
14F:→ cole945: GCC标准pattern就有extract/insert. LLVM比较笨,但写成 03/18 22:44
15F:→ cole945: bitfield其实对LLVM match没帮助 XD 03/18 22:44
16F:→ cole945: 第一部份有点算a64的指令offset范围的关系, compiler不好 03/18 22:47
17F:→ cole945: 处理. 试用mips或risc-v,high-part可以共用, low-part 03/18 22:48
18F:→ cole945: 刚好可以encode到ld/st的offset, 所以两种写法其实会生出 03/18 22:48
19F:→ cole945: 一模一样的code 03/18 22:48
20F:→ cole945: 另外, 第一个问题其实LLVM会处理 03/18 22:49
21F:→ cole945: 不过有点难讲,如果用单一base,会拉长register live range 03/18 22:52
22F:→ cole945: 搞不好会生出比较差的code? GCC会在决定register後才再 03/18 22:53
23F:→ cole945: 尝试做这样的optimize,可能是gcc的策略吧.. 03/18 22:54
24F:→ cole945: 以你的例子,因为是配到x3,x4,x5,所以gcc会消不掉 03/18 22:55
25F:→ cole945: 配相同的reg会有depdency问题,配不同reg可以平行执行.. 03/18 22:55
26F:→ cole945: 有时难讲怎样比较好 orz 03/18 22:55
27F:→ cole945: 我手边gcc,用Os会被消掉其中一组 03/18 22:57
28F:→ hsnuer1171: 太专业了!! 感谢大家的建议 小弟再研读一下以免误导大 03/18 23:28
29F:→ hsnuer1171: 家 03/18 23:28
30F:推 boss0405: 我也不喜欢用bitfield+1,光移植问题就很麻烦了 03/20 12:52
31F:推 ab4daa: 推 03/25 16:31







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:e-shopping站内搜寻

TOP