ZHCADC4A September 2011 – March 2014
编译器生成对辅助函数的调用以执行编译器需要支持但架构不直接支持的运算,例如在缺少专用硬件的设备上执行浮点运算。这些辅助函数必须在符合 ABI 的任何工具链的 RTS 库中实现。
辅助函数使用前缀 _ _C6000 命名。具有此前缀的任何标识符都将保留供 ABI 使用。此外,需要 _ _tls_get_addr() 辅助函数来支持对线程局部存储的动态链接访问。
辅助函数遵守标准调用约定,节 8.3中指定的约定除外。
下表使用 C 表示法和语法指定辅助函数。表中的类型对应于节 2.1中指定的通用数据类型。
表 8-1 中的函数根据 C 语言的转换规则和节 8.1指定的浮点行为将浮点值转换为 int 值。
签名 | 说明 |
---|---|
int32 _ _C6000_fixdi(float64 x); | 将 float64 转换为 int32 |
int40 _ _C6000_fixdli(float64 x); | 将 float64 转换为 int40 |
int64 _ _C6000_fixdlli(float64 x); | 将 float64 转换为 int64 |
uint32 _ _C6000_fixdu(float64 x); | 将 float64 转换为 uint32 |
uint40 _ _C6000_fixdul(float64 x); | 将 float64 转换为 uint40 |
uint64 _ _C6000_fixdull(float64 x); | 将 float64 转换为 uint64 |
int32 _ _C6000_fixfi(float32 x); | 将 float32 转换为 int32 |
int40 _ _C6000_fixfli(float32 x); | 将 float32 转换为 int40 |
int64 _ _C6000_fixflli(float32 x); | 将 float32 转换为 int64 |
uint32 _ _C6000_fixfu(float32 x); | 将 float32 转换为 uint32 |
uint40 _ _C6000_fixful(float32 x); | 将单精度浮点型转换为 uint40 |
uint64 _ _C6000_fixfull(float32 x); | 将单精度浮点型转换为 uint64 |
表 8-2 中的函数根据 C 语言的转换规则和节 8.1指定的浮点行为将 int 值转换为浮点型值。
签名 | 说明 |
---|---|
float64 _ _C6000_fltid(int32 x); | 将 int32 转换为双精度浮点型 |
float64 _ _C6000_fltlid(int40 x); | 将 int40 转换为双精度浮点型 |
float64 _ _C6000_fltllid(int64 x); | 将 int64 转换为双精度浮点型 |
float64 _ _C6000_fltud(uint32 x); | 将 uint32 转换为双精度浮点型 |
float64 _ _C6000_fltuld(uint40 x); | 将 uint40 转换为双精度浮点型 |
float64 _ _C6000_fltulld(uint64 x); | 将 uint64 转换为双精度浮点型 |
float32 _ _C6000_fltif(int32 x); | 将 int32 转换为单精度浮点型 |
float32 _ _C6000_fltlif(int40 x); | 将 int40 转换为单精度浮点型 |
float32 _ _C6000_fltllif(int64 x); | 将 int64 转换为单精度浮点型 |
float32 _ _C6000_fltuf(uint32 x); | 将 uint32 转换为单精度浮点型 |
float32 _ _C6000_fltulf(uint40 x); | 将 uint40 转换为单精度浮点型 |
float32 _ _C6000_fltullf(uint64 x); | 将 uint64 转换为单精度浮点型 |
表 8-3 中的函数根据 C 语言的转换规则和节 8.1指定的浮点行为将浮点型值从一种格式转换为另一种格式。
签名 | 说明 |
---|---|
float32 _ _C6000_cvtdf(float64 x); | 将双精度浮点型转换为单精度浮点型 |
float64 _ _C6000_cvtfd(float32 x); | 将单精度浮点型转换为双精度浮点型 |
表 8-4 中的函数根据 C 语言的语义和节 8.1指定的浮点行为执行浮点运算。
签名 | 说明 |
---|---|
float64 _ _C6000_absd(float64 x); | 返回双精度浮点数的绝对值 |
float32 _ _C6000_absf(float32 x); | 返回单精度浮点数的绝对值 |
float64 _ _C6000_addd(float64 x, float64 y); | 将两个双精度浮点数相加 (x+y) |
float32 _ _C6000_addf(float32 x, float32 y); | 将两个单精度浮点数相加 (x+y) |
float64 _ _C6000_divd(float64 x, float64 y); | 将两个双精度浮点数相除 (x/y) |
float32 _ _C6000_divf(float32 x, float32 y); | 将两个单精度浮点数相除 (x/y) |
float64 _ _C6000_mpyd(float64 x, float64 y); | 将两个双精度浮点数相乘 (x*y) |
float32 _ _C6000_mpyf(float32 x, float32 y); | 将两个单精度浮点数相乘 (x*y) |
float64 _ _C6000_negd(float64 x); | 返回取反的双精度浮点数 (-x) |
float32 _ _C6000_negf(float32 x); | 返回取反的单精度浮点数 (-x) |
float64 _ _C6000_subd(float64 x, float64 y); | 将两个双精度浮点数相减 (x-y) |
float32 _ _C6000_subf(float32 x, float32 y); | 将两个单精度浮点数相减 (x-y) |
int64 _ _C6000_trunc(float64 x); | 将双精度浮点数趋零截尾 |
int32 _ _C6000_truncf(float32 x); | 将单精度浮点数趋零截尾 |
表 8-5 中的函数根据 C 语言的语义和节 8.1指定的浮点行为执行浮点比较。
如果 x 小于 y,则C6000_cmp* 函数返回一个小于 0 的整数;如果 x 等于 y,则返回 0;如果 x 大于 y,则返回一个大于 0 的整数。如果任一操作数为 NaN,则结果未定义。
显式比较函数与无序 (NaN) 操作数可正常运行。也就是说,如果比较结果为 true,则它们返回非零值,即使其中一个操作数为 NaN 也不例外,否则返回 0。
签名 | 说明 |
---|---|
int32 _ _C6000_cmpd(float64 x, float64 y); | 双精度比较 |
int32 _ _C6000_cmpf(float32 x, float32 y); | 单精度比较 |
int32 _ _C6000_unordd(float64 x, float64 y); | 针对无序操作数的双精度检查 |
int32 _ _C6000_unordf(float32 x, float32 y); | 针对无序操作数的单精度检查 |
int32 _ _C6000_eqd(float64 x, float64 y); | 双精度比较:x == y |
int32 _ _C6000_eqf(float32 x, float32 y); | 单精度比较:x == y |
int32 _ _C6000_neqd(float64 x, float64 y); | 双精度比较:x != y |
int32 _ _C6000_neqf(float32 x, float32 y); | 单精度比较:x != y |
int32 _ _C6000_ltd(float64 x, float64 y); | 双精度比较:x < y |
int32 _ _C6000_ltf(float32 x, float32 y); | 单精度比较:x < y |
int32 _ _C6000_gtd(float64 x, float64 y); | 双精度比较:x > y |
int32 _ _C6000_gtf(float32 x, float32 y); | 单精度比较:x > y |
int32 _ _C6000_led(float64 x, float64 y); | 双精度比较:x <= y |
int32 _ _C6000_lef(float32 x, float32 y); | 单精度比较:x <= y |
int32 _ _C6000_ged(float64 x, float64 y); | 双精度比较:x >= y |
int32 _ _C6000_gef(float32 x, float32 y); | 单精度比较:x >= y |
表 8-6 中的整数除法和余数函数根据 C 语言的语义进行运算。
_ _C6000_divremi 和 _ _C6000_divremu 函数计算商 (x/y) 和余数 (x%y)。商在 A4 中返回,余数在 A5 中返回。_ _C6000_divremll 和 _ _C6000_divremull 函数计算 64 位整数的商 (x/y) 和余数 (x%y)。商在 A5:A4 中返回,余数在 B5:B4 中返回。
签名 | 说明 |
---|---|
int32 _ _C6000_divi(int32 x, int32 y); | 32 位 signed int 除法 (x/y) |
int40 _ _C6000_divli(int40 x, int40 y); | 40 位 signed int 除法 (x/y) |
int64 _ _C6000_divlli(int64 x, int64 y); | 64 位 signed int 除法 (x/y) |
uint32 _ _C6000_divu(uint32 x, uint32 y); | 32 位 unsigned int 除法 (x/y) |
uint40 _ _C6000_divlu(uint40 x, uint40 y); | 40 位 unsigned int 除法 (x/y) |
uint64 _ _C6000_divllu(uint64 x, uint64 y); | 64 位 unsigned int 除法 (x/y) |
int32 _ _C6000_remi(int32 x, int32 y); | 32 位 signed int 取模 (x%y) |
int40 _ _C6000_remli(int40 x, int40 y); | 40 位 signed int 取模 (x%y) |
int64 _ _C6000_remlli(int64x. int64 y); | 64 位 signed int 取模 (x%y) |
uint32 _ _C6000_remu(uint32 x, uint32 y); | 32 位 unsigned int 取模 (x%y) |
uint40 _ _C6000_remul(uint40, uint40); | 40 位 unsigned int 取模 (x%y) |
uint64 _ _C6000_remull(uint64, uint64); | 64 位 unsigned int 取模 (x%y) |
_ _C6000_divremi(int32 x, int32 y); | 32 位组合除法和模数 |
_ _C6000_divremu(uint32 x, uint32 y); | 32 位无符号组合除法和模数 |
_ _C6000_divremull(uint64 x, uint64 y); | 64 位无符号组合除法和模数 |
表 8-7 中的宽整数算术函数根据 C 语言的语义进行运算。
签名 | 说明 |
---|---|
int64 _ _C6000_negll(int64 x); | 64 位整数取反 |
uint64 _ _C6000_mpyll(uint64 x, uint64 y); | 64x64 位相乘 |
int64 _ _C6000_mpyiill(int32 x, int32 y); | 32x32 位相乘 |
uint64 _ _C6000_mpyuiill(uint32 x, uint32 y); | 32x32 位无符号相乘 |
int64 _ _C6000_llshr(int64 x, uint32 y); | 64 位有符号向右移位 (x>>y) |
uint64 _ _C6000_llshru(uint64 x, uint32 y); | 64 位无符号向右移位 (x>>y) |
uint64 _ _C6000_llshl(uint64 x, uint32 y); | 64 位向左移位 (x<<y) |
下面的章节将介绍表 8-8 中的其他辅助函数。
签名 | 说明 |
---|---|
void _ _C6000_strasgi(int32 *dst, const int32 *src, uint32 cnt); | 中断安全块复制;cnt >= 28 |
void _ _C6000_strasgi_64plus(int32*, const inst32*, uint32) ; | 中断安全块复制;cnt >= 28 |
void _ _C6000_abort_msg(const char *string); | 报告断言失败 |
void _ _C6000_push_rts(void); | 压入所有被调用者保存的寄存器 |
void _ _C6000_pop_rts(void); | 弹出所有被调用者保存的寄存器 |
void _ _C6000_call_stub(void); | 保存调用者保存的寄存器;调用 B31 |
void _ _C6000_weak_return(void); | 导入的弱调用的解析目标 |
void _ _C6000_get_addr(ptrdiff_t TPR_offst); | 获取线程指针寄存器 (TPR) 偏移量的地址。 |
void _ _C6000_get_tp(void); | 获取当前线程的线程指针值。 |
void * _ _tls_get_addr(struct TLS_descriptor); | 获取线程局部变量的地址。 |
_ _C6000_strasgi
函数 _ _C6000_strasgi 由编译器为高效行外结构或数组复制操作生成。cnt 实参表示大小(以字节为单位),它必须是 4 的倍数且大于或等于 28(7 个字)。它进行以下假设:
7 个字的最小值是允许在 C64x+上使用软件流水线循环的阈值。对于较小的对象,编译器通常会生成内联序列的加载/存储指令。_ _C6000_strasgi 不会禁用中断,并且可以安全地中断。
函数 _ _C6000_strasgi_64plus 是 _ _C6000_strasgi 针对 C64x+架构进行了优化的版本。
_ _C6000_abort_msg
生成函数 _ _C6000_abort_msg 是为了在运行时断言(例如 C 断言宏)失败时输出诊断消息。该函数不得返回。也就是说,该函数必须调用中止或通过其他方式终止程序。
_ _C6000_push_rts 和 _ _C6000_pop_rts
在优化代码大小时,在 C64x+ 架构上使用函数 _ _c6x_push_rts。许多函数可保存和恢复大多数或所有被调用者保存的寄存器。为避免复制逻辑程序中的保存代码,并在每个此类函数的收尾程序中恢复代码,编译器可改为采用此库函数。该函数将所有 13 个被调用者保存的寄存器压入堆栈,并根据节 4.4.4中的协议将 SP 递减 56 个字节。
函数 _ _c6x_push_rts 的实现方式如下所示:
__c6xabi_push_rts:
STW B14, *B15--[2]
STDW A15:A14, *B15--
STDW B13:B12, *B15--
STDW A13:A12, *B15--
STDW B11:B10, *B15--
STDW A11:A10, *B15--
STDW B3:B2, *B15--
B A3
(这是连续的未调度表示法。请参考 TI 运行时库中的源代码以了解实际实现。)
函数 _ _C6000_pop_rts 会根据 _ _C6000_push_rts 压入的方式恢复被调用者保存的寄存器,并将栈递增(弹出)56 个字节。
_ _C6000_call_stub
函数 _ _C6000_call_stub 还用于帮助优化 C64x+ 函数的代码大小。许多调用站点都有多个在整个调用期间活跃的调用者保存的寄存器。这些寄存器不会由调用保留,因此必须由调用者保存和恢复。编译器可以通过 _ _C6000_call_stub 路由调用,该函数会执行以下运算序列:
这样,所选的寄存器会在整个调用中保留,而无需调用者保存和恢复它们。由 _ _C6000_call_stub 保留的寄存器有:A0、A1、A2、A6、A7、B0、B1、B2、B4、B5、B6 和 B7。
调用者调用 _ _C6000_call_stub 的方式是将要调用的函数的地址放置在 B31中,然后跳转到 _ _C6000_call_stub。(如往常一样,返回地址在 B3 中。)
函数 _ _C6000_call_stub 的实现方式如下:
__c6xabi_call_stub:
STW A2, *B15--[2]
STDW A7:A6, *B15--
STDW A1:A0, *B15--
STDW B7:B6, *B15--
STDW B5:B4, *B15--
STDW B1:B0, *B15--
STDW B3:B2, *B15--
ADDKPC __STUB_RET, B3, 0
CALL B31
__STUB_RET:
LDDW *++B15, B3:B2
LDDW *++B15, B1:B0
LDDW *++B15, B5:B4
LDDW *++B15, B7:B6
LDDW *++B15, A1:A0
LDDW *++B15, A7:A6
LDW *++B15[2], A2
B B3
(这是连续的未调度表示法。请参考 TI 运行时库中的源代码以了解实际实现。)
由于 _ _C6000_call_stub 使用非标准约定,因此无法通过 PLT 条目调用它。其在库中的定义必须标记为 STV_INTERNAL 或 STV_HIDDEN,以防止从共享库中导入它。
_ _C6000_weak_return
函数 _ _C6000_weak_return 是只返回的函数。链接器应将其包含在动态可执行文件或共享对象中(其包含对导入的弱符号的任何未解析调用)。如果这些调用在动态加载时保持未解析状态,动态链接器可以使用它来解析这些调用。
_ _C6000_get_addr
函数 _ _C6000_get_addr 接受 32 位 TPR 偏移量并返回线程局部地址。特殊值 -1 用于指示未定义的弱引用,并且在这种情况下返回 0。在编译静态可执行文件和裸机动态 TLS 访问模型时使用此函数。有关线程局部存储的详细信息,请参阅Chapter7。
_ _C6000_get_tp
函数 _ _C6000_get_tp 返回当前线程的线程指针值。此函数不修改返回寄存器 A4 以外的任何寄存器。此函数可以通过 PLT 调用,因此调用者应假设 B30 和 B31 已通过调用此函数进行修改。有关线程局部存储的详细信息,请参阅Chapter7和节 14.1.4。
_ _tls_get_addr
函数 _ _tls_get_addr 返回线程局部变量的地址。有关此函数以及传递给它的 TLS_descriptor 结构以指定线程局部变量的偏移量的详细信息,请参阅节 7.5.1.1。当编译静态可执行文件和裸机动态 TLS 访问模型以外的所有访问模型时使用此函数。有关线程局部存储的详细信息,请参阅Chapter7。