使用 gcc 在裸机 ARM 上进行 RAM 和 ROM 部分之间的长调用

问题描述 投票:0回答:2

没关系,这个历史问题与 gcc 4.3 之后没有任何关系

c
2个回答
3
投票

我怀疑没有办法自动做你想做的事。我确实有一个建议,尽管它并不完全是你想要的。

像这样声明 RAM 函数(例如在 ram_funcs.h 中):

int foo() RAM_CALL;

然后将所有 ROM 函数放入各自的文件中,并像这样启动每个文件:

#define RAM_CALL __attribute__((__long_call__))
#include "ram_funcs.h"

其他文件会使用:

#define RAM_CALL
#include "ram_funcs.h"

这并不比使用

-mlong-calls
好多少,但至少它将逻辑放在函数本身附近,而不是放在某些编译选项中。例如,可以更轻松地将一些函数放入每个 .c 文件中。


-1
投票

以防万一其他人像我一样偶然发现这个问题,这是我想出的解决方案。

就像 Eric Angell 所说,理想情况下您可以指定您希望通过长调用来调用函数。因为无法使用 GCC 在 C/C++ 中直接执行此操作,所以我转向汇编器并编写了一些宏来拦截 GCC 生成的 bl 和 blx 程序集并用长调用替换它们。

//longCall.h
#pragma once

#define INLINE inline __attribute__((always_inline))

template<typename T1, typename T2>
struct IsType {
    constexpr operator bool() { return false; }
};

template<typename T>
struct IsType<T, T> {
    constexpr operator bool() { return true; }
};

INLINE void EnableLongCallBranches() {
    asm volatile(R"(
        interceptBranchAndConditions bl
        interceptBranchAndConditions blx
    )");
}

INLINE void DisableLongCallBranches() {
    asm volatile(R"(
        disableInterceptBranchAndConditions bl
        disableInterceptBranchAndConditions blx
    )");
}

template<auto func, typename... ArgsT>
INLINE auto LongCall(ArgsT... args) {

    EnableLongCallBranches();

    //Check if function call returns void
    if constexpr(IsType<decltype(func(args...)), void>()) {
    
        //just evalute function
        func(args...);
        DisableLongCallBranches();
    
    } else {

        //return result of function
        //Note: cannot simply use 'return func(args...)' or else compiler will treat 
        //      'DisableLongCallBranches()' as dead code and remove it 
        auto val = func(args...);
        DisableLongCallBranches();
        return val;
    }
}

//Static assembly macros to intercept and replace relative branches with long jumps
asm(R"(
    
    @ macro for intercepting a branch call and replacing them with calls to longCallIntercept
    .macro interceptBranch instruction, condition
        .macro \instruction\condition label
            longCallIntercept \instruction, \condition, \label
        .endm
    .endm
    
    @ macro for intercepting a branch call and all of its conditnal calls with longCallIntercept
    .macro interceptBranchAndConditions instruction
        interceptBranch \instruction, "eq" @ Equal. Z==1
        interceptBranch \instruction, "ne" @ Not equal. Z==0
        interceptBranch \instruction, "cs" @ Unsigned higher or same (or carry set). C==1
        interceptBranch \instruction, "hs" @ Unsigned higher or same (or carry set). C==1
        interceptBranch \instruction, "cc" @ Unsigned lower (or carry clear). C==0
        interceptBranch \instruction, "lo" @ Unsigned lower (or carry clear). C==0
        interceptBranch \instruction, "mi" @ Negative. The mnemonic stands for "minus". N==1
        interceptBranch \instruction, "pl" @ Positive or zero. The mnemonic stands for "plus". N==0
        interceptBranch \instruction, "vs" @ Signed overflow. The mnemonic stands for "V set". V==1
        interceptBranch \instruction, "vc" @ No signed overflow. The mnemonic stands for "V clear". V==0
        interceptBranch \instruction, "hi" @ Unsigned higher. (C==1) && (Z==0)
        interceptBranch \instruction, "ls" @ Unsigned lower or same. (C==0) || (Z==1)
        interceptBranch \instruction, "ge" @ Signed greater than or equal. N==V
        interceptBranch \instruction, "lt" @ Signed less than. N!=V
        interceptBranch \instruction, "gt" @ Signed greater than. (Z==0) && (N==V)
        interceptBranch \instruction, "le" @ Signed less than or equal. (Z==1) || (N!=V)
        interceptBranch \instruction, "al" @ Always executed.
        interceptBranch \instruction, ""   @ Always executed.
    .endm
    
    @ macro for deleting all branch call and condition call interception macros
    .macro disableInterceptBranchAndConditions instruction
        .purgem \instruction\()eq @ Equal.  Z==1
        .purgem \instruction\()ne @ Not equal.  Z==0
        .purgem \instruction\()cs @ Unsigned higher or same (or carry set). C==1
        .purgem \instruction\()hs @ Unsigned higher or same (or carry set). C==1
        .purgem \instruction\()cc @ Unsigned lower (or carry clear). C==0
        .purgem \instruction\()lo @ Unsigned lower (or carry clear). C==0
        .purgem \instruction\()mi @ Negative. The mnemonic stands for "minus".  N==1
        .purgem \instruction\()pl @ Positive or zero. The mnemonic stands for "plus". N==0
        .purgem \instruction\()vs @ Signed overflow. The mnemonic stands for "V set". V==1
        .purgem \instruction\()vc @ No signed overflow. The mnemonic stands for "V clear".  V==0
        .purgem \instruction\()hi @ Unsigned higher. (C==1) && (Z==0)
        .purgem \instruction\()ls @ Unsigned lower or same. (C==0) || (Z==1)
        .purgem \instruction\()ge @ Signed greater than or equal. N==V
        .purgem \instruction\()lt @ Signed less than. N!=V
        .purgem \instruction\()gt @ Signed greater than. (Z==0) && (N==V)
        .purgem \instruction\()le @ Signed less than or equal.  (Z==1) || (N!=V)
        .purgem \instruction\()al @ Always executed.
        .purgem \instruction      @ Always executed.
    .endm
    
    @ macro for replacing relative jumps with long jumps
    .macro longCallIntercept instruction, condition, label
        
        @ check if label is register
        .set _longCallIntercept_LabelIsReg, 0
        .irp register r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, ip, lr, pc
            .ifeqs "\label", "\register"
                .set _longCallIntercept_LabelIsReg, 1
            .endif
        .endr

        @ delete instruction macro so 'instruction' is treated as opcode
        .purgem blx\condition
    
        .if _longCallIntercept_LabelIsReg
    
            .warning "Ignoring: [\instruction \label] long call"
    
            @ keep existing branch to register instruction
            \instruction\condition \label
    
        .else

            .print "Replacing: [\instruction \label] with long call"
        
            @ invoke long branch
            @ Note: could use 'ldr ip, =\label' to favor size over speed
            movw ip, #:lower16:\label
            movt ip, #:upper16:\label
            blx\condition ip
    
        .endif
 
        @ Redefine instruction macro
        interceptBranch blx, \condition
    .endm
)");

现在,您可以在代码中的任何位置明确指定您希望哪些调用时间较长。

//example.cpp
#include "longCall.h"

//Note: Only marked noinline to prevent optimize from evaluating
//      function at compiletime
__attribute__((noinline)) int foo(int x) { 
    return x;
}

int main() {
    
    int v1 = LongCall<foo>(1);
    int v2 = foo(2);   

    return v1 + v2;
}

输出组件:

foo(int):
    bx      lr

main:
    push    {r4, lr}
    
    @ long call to foo(1) 
    movs    r0, #1
    movw    ip, #445    ; 0x1bd
    movt    ip, #1
    blx     ip
    
    mov     r4, r0
    
    @ short call to foo(2)
    movs    r0, #2
    bl      101bc       ; <foo(int)>
    
    add     r0, r4
    pop     {r4, pc}

这是上述示例的编译器资源管理器。

© www.soinside.com 2019 - 2024. All rights reserved.