是否可以使这个混合的c++/asm函数符合标准?函数ePendSV()必须具有以下布局:
ePendSV: //function entry point
mrs r0,PSP
stmdb r0!,{r4-r11,lr}
// compiler can generate any code here doing these things:
readyTcbQueue.pTcb.runTcb->psp = r0;
readyTcbQueue.pTcb.runTcb=readyTcbQueue.pTcb.readyTcb;
r0 = readyTcbQueue.pTcb.readyTcb->psp;
// work with r0 in assembly
ldmia r0!,{r4-r11,lr}
msr PSP,r0
bx lrreadyTcbQueue.pTcb是一个简单的struct对象,只有两个指向BragOsTcb对象的指针;
struct{
BragOsTcb *runTcb;
BragOsTcb *readyTcb;
};BragOsTcb是非POD类,但没有虚拟函数和虚拟继承,如下所示:
class BragOsTcb : public TcbCdllq, public TimerTcbCdllq, public BragOsObject{
public:
BragOsTcb();
....
private:
.....
public:
unsigned long psp;
....
};TcbCdllq、TimerTcbCdllq、BragOsObject也是布局相似、没有虚拟化的简单类。但它们也是非POD的.
我做了这个代码,它工作在gcc和clang,但它是一个非标准的黑客,可能不起作用。
__attribute__((naked)) void ePendSV(){
asm volatile("\
mrs r0,PSP \n\
stmdb r0!,{r4-r11,lr} \n\
\n\
ldr r1,=%0 \n\
ldmia r1,{r2,r3} // r2=runTcb, r3=readyTcb \n\
str r0,[r2,%1] // save psp \n\
str r3,[r1,#0] // runTcb=readyTcb \n\
ldr r0,[r3,%1] // readyTcb->psp \n\
\n\
// restore LR(EXC_RETURN),R11-R4 from new PSP, set new PSP, return \n\
ldmia r0!,{r4-r11,lr} \n\
msr PSP,r0 \n\
bx lr \n\
" :
: "i"(&readyTcbQueue.pTcb),"i"(&(((BragOsTcb*)0)->psp))
: );
// it'is an offsetof hack which might not work
}谢谢!
发布于 2015-10-16 22:09:11
由于扩展的asm在裸功能上不支持gcc/clang
裸此属性允许编译器构造必要的函数声明,同时允许函数的主体为程序集代码。指定的函数将不具有编译器生成的序言/结语序列。只有基本的asm语句可以安全地包含在裸函数中(参见基本Asm)。虽然使用扩展的asm或基本的asm和C代码的混合物似乎可以工作,但它们不能依赖于可靠地工作,并且不受支持。https://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html#ARM-Function-Attributes
我们可以使用正常的C++函数,然后从裸的跳/调用它。
typedef unsigned long U32;
__attribute__((naked)) void ePendSV(){
asm volatile("\
mrs r0, PSP \n\
stmdb r0!, {r4-r11,lr} \n\
b realSwitchContext"); // this solution costs single jump instruction named **b**. It's valid for ARM-assembly in this case.
// Stack pointer has been saved in r0 register. we dont care about stack for now.
}
extern "C" void realSwitchContext(U32 psp){
readyTcbQueue.pTcb.runTcb->psp = psp;
BragOsTcb *tcb = readyTcbQueue.pTcb.readyTcb;
readyTcbQueue.pTcb.runTcb = tcb;
psp = tcb->psp;
asm volatile("\
ldmia %0!, {r4-r11,lr} \n\
msr PSP, %0 \n\
bx lr" : : "r"(psp)); // we just changed stack pointer then can return here and don't care about C++ stacked data
}由于有两个堆栈指针- MSP和PSP,在上面的实现中有一些缺陷。如果bx lr将使用堆栈,MSP将被最后的C++指令破坏。下面的解决方案是正确的,但花费2‘昂贵’指令- bl (呼叫)和bx lr (返回)
__attribute__((naked)) void ePendSV(){
asm volatile("\
mrs r0, PSP \n\
stmdb r0!, {r4-r11,lr} \n\
bl realSwitchContext \n\
ldmia r0!, {r4-r11,lr} \n\
msr PSP, r0 \n\
bx lr");
}
extern "C" U32 realSwitchContext(U32 psp){
readyTcbQueue.pTcb.runTcb->psp = psp;
BragOsTcb *tcb = readyTcbQueue.pTcb.readyTcb;
readyTcbQueue.pTcb.runTcb = tcb;
return tcb->psp;
}https://stackoverflow.com/questions/33175120
复制相似问题