先来看看我们SmartIRQ的具体实现
// 智能IRQ,初始化时备份,销毁时还原 class SmartIRQ { public: force_inline SmartIRQ(bool enable = false) { _state = __get_PRIMASK(); if(enable) __enable_irq(); else __disable_irq(); } force_inline ~SmartIRQ() { __set_PRIMASK(_state); } private: uint _state; };
在构造的时候备份,然后根据参数决定打开还是关闭中断。
在系统内核时钟里面,关键操作需要关闭中断,最后打开,以免其它中断影响关键操作的原子事务性。
于是我们有:
ulong Time::CurrentTicks() { SmartIRQ irq; uint value = (SysTick->LOAD - SysTick->VAL); if(SysTick->CTRL & SysTick_CTRL_COUNTFLAG) { Ticks += SysTick->LOAD; } return Ticks + value; }
其中irq在离开作用域时被释放,自动调用SmartIRQ的析构函数,还原了中断状态
因为调用极其频繁,最高可能1us调用一次该函数,于是我们给SmartIRQ的构造和析构都加了force_inline强制使用内联。
总所周知,C++的内联其实就是以空间换时间,把一个函数的代码全部搬出来直接使用,省去了调用、压栈、弹栈、返回等操作。
SmartIRQ的析构函数就罢了,但是构造函数代码量还是有好几行的。
怀着试一试的心态调试该函数,直接观察汇编代码:
0x08000804 B570 PUSH {r4-r6,lr} 0x08000806 F3EF8210 MRS r2,PRIMASK 0x0800080A B672 CPSID I 0x0800080C 4D0B LDR r5,[pc,#44] ; @0x0800083C 0x0800080E 6969 LDR r1,[r5,#0x14] 0x08000810 69AB LDR r3,[r5,#0x18] 0x08000812 1ACC SUBS r4,r1,r3 0x08000814 6929 LDR r1,[r5,#0x10] 0x08000816 2300 MOVS r3,#0x00 0x08000818 03C9 LSLS r1,r1,#15 0x0800081A 2900 CMP r1,#0x00 0x0800081C DA06 BGE 0x0800082C 0x0800081E 6886 LDR r6,[r0,#0x08] 0x08000820 68C1 LDR r1,[r0,#0x0C] 0x08000822 696D LDR r5,[r5,#0x14] 0x08000824 1975 ADDS r5,r6,r5 0x08000826 4159 ADCS r1,r1,r3 0x08000828 6085 STR r5,[r0,#0x08] 0x0800082A 60C1 STR r1,[r0,#0x0C] 0x0800082C 6885 LDR r5,[r0,#0x08] 0x0800082E 68C1 LDR r1,[r0,#0x0C] 0x08000830 1928 ADDS r0,r5,r4 0x08000832 4159 ADCS r1,r1,r3 0x08000834 F3828810 MSR PRIMASK,r2 0x08000838 BD70 POP {r4-r6,pc}
MDK C++编译器优化到了极度变态的地步!
不仅仅内联了,SmartIRQ里面有两个分支语句,直接被他省略了其中一个,因为参数true已经确定。
更加变态的是,本来采用SmartIRQ内部私有成员_state保存状态,析构时恢复的,它直接把这个状态保存到寄存器r2里面去,连_state的内存都给省了。