ZHCADC4A September   2011  – March 2014

 

  1.   1
  2. 简介
    1. 1.1  ABI - C6000
    2. 1.2  范围
    3. 1.3  ABI 变体
    4. 1.4  工具链和互操作性
    5. 1.5 
    6. 1.6  目标文件的类型
    7. 1.7 
    8. 1.8  C6000 架构概述
    9. 1.9  参考文档
    10. 1.10 代码片段表示法
  3. 数据表示
    1. 2.1 基本类型
    2. 2.2 寄存器中的数据
    3. 2.3 存储器中的数据
    4. 2.4 复数类型
    5. 2.5 结构体和联合体
    6. 2.6 数组
    7. 2.7 位字段
      1. 2.7.1 易失性位字段
    8. 2.8 枚举类型
  4. 调用约定
    1. 3.1 调用和返回
      1. 3.1.1 返回地址计算
      2. 3.1.2 调用指令
      3. 3.1.3 返回指令
      4. 3.1.4 流水线约定
      5. 3.1.5 弱函数
    2. 3.2 寄存器惯例
    3. 3.3 实参传递
    4. 3.4 返回值
    5. 3.5 通过引用传递并返回的结构体和联合体
    6. 3.6 编译器辅助函数的约定
    7. 3.7 段间调用的暂存寄存器
    8. 3.8 设置 DP
  5. 数据分配和寻址
    1. 4.1 数据段和数据区段
    2. 4.2 静态数据的分配和寻址
      1. 4.2.1 静态数据的寻址方法
        1. 4.2.1.1 near DP 相对寻址
        2. 4.2.1.2 Far DP 相对寻址
        3. 4.2.1.3 绝对寻址
        4. 4.2.1.4 GOT 间接寻址
        5. 4.2.1.5 PC 相对寻址
      2. 4.2.2 静态数据的放置约定
        1. 4.2.2.1 放置的抽象约定
        2. 4.2.2.2 寻址的抽象约定
        3. 4.2.2.3 链接器要求
      3. 4.2.3 静态数据的初始化
    3. 4.3 自动变量
    4. 4.4 帧布局
      1. 4.4.1 栈对齐
      2. 4.4.2 寄存器保存顺序
        1. 4.4.2.1 大端字节序对交换
        2. 4.4.2.2 示例
      3. 4.4.3 DATA_MEM_BANK
      4. 4.4.4 C64x+ 特定的堆栈布局
        1. 4.4.4.1 _ _C6000_push_rts 布局
        2. 4.4.4.2 紧凑帧布局
    5. 4.5 堆分配对象
  6. 代码分配和寻址
    1. 5.1 计算代码标签的地址
      1. 5.1.1 代码的绝对寻址
      2. 5.1.2 PC 相对寻址
      3. 5.1.3 同一段内的 PC 相对寻址
      4. 5.1.4 短偏移 PC 相对寻址 (C64x)
      5. 5.1.5 基于 GOT 的代码寻址
    2. 5.2 分支
    3. 5.3 调用
      1. 5.3.1 直接 PC 相对调用
      2. 5.3.2 Far Call Trampoline
      3. 5.3.3 间接调用
    4. 5.4 寻址紧凑指令
  7. 动态链接的寻址模型
    1. 6.1 术语和概念
    2. 6.2 动态链接机制概述
    3. 6.3 DSO 和 DLL
    4. 6.4 抢占
    5. 6.5 PLT 条目
      1. 6.5.1 直接调用导入函数
      2. 6.5.2 通过绝对地址寻址的 PLT 条目
      3. 6.5.3 通过 GOT 寻址的 PLT 条目
    6. 6.6 全局偏移表
      1. 6.6.1 使用 Near DP 相对寻址的基于 GOT 的引用
      2. 6.6.2 使用 Far DP 相对寻址的基于 GOT 的引用
    7. 6.7 DSBT 模型
      1. 6.7.1 导出函数的进入/退出序列
      2. 6.7.2 避免在内部函数中使用 DP 负载
      3. 6.7.3 函数指针
      4. 6.7.4 中断
      5. 6.7.5 与非 DSBT 代码的兼容性
    8. 6.8 动态链接的性能影响
  8. 线程局部存储分配和寻址
    1. 7.1 关于多线程和线程局部存储
    2. 7.2 术语和概念
    3. 7.3 用户界面
    4. 7.4 ELF 目标文件表示
    5. 7.5 TLS 访问模型
      1. 7.5.1 C6x Linux TLS 模型
        1. 7.5.1.1 通用动态 TLS 访问模型
        2. 7.5.1.2 局部动态 TLS 访问模型
        3. 7.5.1.3 初始可执行文件 TLS 访问模型
          1. 7.5.1.3.1 线程指针
          2. 7.5.1.3.2 初始可执行文件 TLS 寻址
        4. 7.5.1.4 局部可执行文件 TLS 访问模型
      2. 7.5.2 静态可执行文件 TLS 模型
        1. 7.5.2.1 静态可执行文件寻址
        2. 7.5.2.2 静态可执行文件 TLS 运行时架构
        3. 7.5.2.3 静态可执行文件 TLS 分配
          1. 7.5.2.3.1 TLS 初始化映像分配
          2. 7.5.2.3.2 主线程的 TLS 分配
          3. 7.5.2.3.3 线程库的 TLS 区域分配
        4. 7.5.2.4 静态可执行文件 TLS 初始化
          1. 7.5.2.4.1 主线程的 TLS 初始化
          2. 7.5.2.4.2 线程库进行 TLS 初始化
        5. 7.5.2.5 线程指针
      3. 7.5.3 裸机动态链接 TLS 模型
        1. 7.5.3.1 用于裸机动态链接的默认 TLS 寻址
        2. 7.5.3.2 TLS 块创建
    6. 7.6 线程局部符号解析和弱引用
      1. 7.6.1 通用和局部动态 TLS 弱引用寻址
      2. 7.6.2 初始和局部可执行文件 TLS 弱引用寻址
      3. 7.6.3 静态可执行文件和裸机动态 TLS 模型弱引用
  9. 辅助函数 API
    1. 8.1 浮点行为
    2. 8.2 C 辅助函数 API
    3. 8.3 辅助函数的特殊寄存器约定
    4. 8.4 复数类型的辅助函数
    5. 8.5 C99 的浮点辅助函数
  10. 标准 C 库 API
    1. 9.1  保留符号
    2. 9.2  <assert.h> 实现
    3. 9.3  <complex.h> 实现
    4. 9.4  <ctype.h> 实现
    5. 9.5  <errno.h> 实现
    6. 9.6  <float.h> 实现
    7. 9.7  <inttypes.h> 实现
    8. 9.8  <iso646.h> 实现
    9. 9.9  <limits.h> 实现
    10. 9.10 <locale.h> 实现
    11. 9.11 <math.h> 实现
    12. 9.12 <setjmp.h> 实现
    13. 9.13 <signal.h> 实现
    14. 9.14 <stdarg.h> 实现
    15. 9.15 <stdbool.h> 实现
    16. 9.16 <stddef.h> 实现
    17. 9.17 <stdint.h> 实现
    18. 9.18 <stdio.h> 实现
    19. 9.19 <stdlib.h> 实现
    20. 9.20 <string.h> 实现
    21. 9.21 <tgmath.h> 实现
    22. 9.22 <time.h> 实现
    23. 9.23 <wchar.h> 实现
    24. 9.24 <wctype.h> 实现
  11. 10C++ ABI
    1. 10.1  限制 (GC++ABI 1.2)
    2. 10.2  导出模板 (GC++ABI 1.4.2)
    3. 10.3  数据布局(GC++ABI 第 2 章)
    4. 10.4  初始化保护变量 (GC++ABI 2.8)
    5. 10.5  构造函数返回值 (GC++ABI 3.1.5)
    6. 10.6  一次性构建 API (GC++ABI 3.3.2)
    7. 10.7  控制对象构造顺序 (GC++ ABI 3.3.4)
    8. 10.8  还原器 API (GC++ABI 3.4)
    9. 10.9  静态数据 (GC++ ABI 5.2.2)
    10. 10.10 虚拟表和键函数 (GC++ABI 5.2.3)
    11. 10.11 回溯表位置 (GC++ABI 5.3)
  12. 11异常处理
    1. 11.1  概述
    2. 11.2  PREL31 编码
    3. 11.3  异常索引表 (EXIDX)
      1. 11.3.1 指向行外 EXTAB 条目的指针
      2. 11.3.2 EXIDX_CANTUNWIND
      3. 11.3.3 内联 EXTAB 条目
    4. 11.4  异常处理指令表 (EXTAB)
      1. 11.4.1 EXTAB 通用模型
      2. 11.4.2 EXTAB 紧凑模型
      3. 11.4.3 个性化例程
    5. 11.5  回溯指令
      1. 11.5.1 通用序列
      2. 11.5.2 字节编码展开指令
      3. 11.5.3 24 位展开编码
    6. 11.6  描述符
      1. 11.6.1 类型标识符编码
      2. 11.6.2 作用域
      3. 11.6.3 Cleanup 描述符
      4. 11.6.4 catch 描述符
      5. 11.6.5 函数异常规范 (FESPEC) 描述符
    7. 11.7  特殊段
    8. 11.8  与非 C++ 代码交互
      1. 11.8.1 EXIDX 条目自动生成
      2. 11.8.2 手工编码的汇编函数
    9. 11.9  与系统功能交互
      1. 11.9.1 共享库
      2. 11.9.2 覆盖块
      3. 11.9.3 中断
    10. 11.10 TI 工具链中的汇编语言运算符
  13. 12DWARF
    1. 12.1 DWARF 寄存器名称
    2. 12.2 调用帧信息
    3. 12.3 供应商名称
    4. 12.4 供应商扩展
  14. 13ELF 目标文件(处理器补充)
    1. 13.1 注册供应商名称
    2. 13.2 ELF 标头
    3. 13.3
      1. 13.3.1 段索引
      2. 13.3.2 段类型
      3. 13.3.3 扩展段标头属性
      4. 13.3.4 子段
      5. 13.3.5 特殊段
      6. 13.3.6 段对齐
    4. 13.4 符号表
      1. 13.4.1 符号类型
      2. 13.4.2 通用块符号
      3. 13.4.3 符号名称
      4. 13.4.4 保留符号名称
      5. 13.4.5 映射符号
    5. 13.5 重定位
      1. 13.5.1 重定位类型
      2. 13.5.2 重定位操作
      3. 13.5.3 未解析的弱引用的重定位
  15. 14ELF 程序加载和动态链接(处理器补充)
    1. 14.1 程序标头
      1. 14.1.1 基址
      2. 14.1.2 段内容
      3. 14.1.3 绑定段和只读段
      4. 14.1.4 线程局部存储
    2. 14.2 程序加载
    3. 14.3 动态链接
      1. 14.3.1 程序解释器
      2. 14.3.2 动态段
      3. 14.3.3 共享对象依赖关系
      4. 14.3.4 全局偏移量表
      5. 14.3.5 过程链接表
      6. 14.3.6 抢占式
      7. 14.3.7 初始化和终止
    4. 14.4 裸机动态链接模型
      1. 14.4.1 文件类型
      2. 14.4.2 ELF 标识
      3. 14.4.3 可见性和绑定
      4. 14.4.4 数据寻址
      5. 14.4.5 代码寻址
      6. 14.4.6 动态信息
  16. 15Linux ABI
    1. 15.1  文件类型
    2. 15.2  ELF 标识
    3. 15.3  程序标头和段
    4. 15.4  数据寻址
      1. 15.4.1 数据区段基表 (DSBT)
      2. 15.4.2 全局偏移量表 (GOT)
    5. 15.5  代码寻址
    6. 15.6  延迟绑定
    7. 15.7  可见性
    8. 15.8  抢占式
    9. 15.9  “作为自有导入”占先
    10. 15.10 程序加载
    11. 15.11 动态信息
    12. 15.12 初始化和终止函数
    13. 15.13 Linux 模型摘要
  17. 16符号版本控制
    1. 16.1 ELF 符号版本控制概述
    2. 16.2 版本段标识
  18. 17构建属性
    1. 17.1 C6000 ABI 构建属性子段
    2. 17.2 C6000 构建属性标签
  19. 18复制表和变量初始化
    1. 18.1 复制表格式
    2. 18.2 压缩的数据格式
      1. 18.2.1 RLE
      2. 18.2.2 LZSS 格式
    3. 18.3 变量初始化
  20. 19扩展程序标头属性
    1. 19.1 编码
    2. 19.2 属性标签定义
    3. 19.3 扩展程序标头属性段格式
  21. 20修订历史记录

C 辅助函数 API

编译器生成对辅助函数的调用以执行编译器需要支持但架构不直接支持的运算,例如在缺少专用硬件的设备上执行浮点运算。这些辅助函数必须在符合 ABI 的任何工具链的 RTS 库中实现。

辅助函数使用前缀 _ _C6000 命名。具有此前缀的任何标识符都将保留供 ABI 使用。此外,需要 _ _tls_get_addr() 辅助函数来支持对线程局部存储的动态链接访问。

辅助函数遵守标准调用约定节 8.3中指定的约定除外

下表使用 C 表示法和语法指定辅助函数。表中的类型对应于节 2.1中指定的通用数据类型。

表 8-1 中的函数根据 C 语言的转换规则和节 8.1指定的浮点行为将浮点值转换为 int 值。

表 8-1 C6000 浮点型至 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 值转换为浮点型值。

表 8-2 C6000 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指定的浮点行为将浮点型值从一种格式转换为另一种格式。

表 8-3 C6000 浮点型格式转换
签名说明
float32 _ _C6000_cvtdf(float64 x);将双精度浮点型转换为单精度浮点型
float64 _ _C6000_cvtfd(float32 x);将单精度浮点型转换为双精度浮点型

表 8-4 中的函数根据 C 语言的语义和节 8.1指定的浮点行为执行浮点运算。

表 8-4 C6000 浮点算术运算
签名说明
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。

表 8-5 浮点数比较
签名说明
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 中返回。

表 8-6 C6000 整数除法和余数
签名说明
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 语言的语义进行运算。

表 8-7 C6000 宽整数算术
签名说明
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 中的其他辅助函数。

表 8-8 C6000 其他辅助函数
签名说明
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 个字)。它进行以下假设:

  • src 和 dst 地址按字对齐。
  • 源对象和目标对象不重叠。

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