没关系,这个历史问题与 gcc 4.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 文件中。
以防万一其他人像我一样偶然发现这个问题,这是我想出的解决方案。
就像 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}
这是上述示例的编译器资源管理器。