首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

PPC入门与优化杂谈

PPC入门与优化杂谈

背景介绍
PowerPC于1991年IBM/MOTO/APPLE研制,大量应用于服务器(AIX / AS400系列及苹果系列服务器),家用游戏机(PS3, Wii, XBOX, GameCube),以及嵌入式(仅次于Arm/x86排第三)。PowerPC核心在于开放系统软件标准,其应用范围仅次于x86,是除去x86外最值得开发者了解的体系。
不需要写出非常高效的代码,但要了解基本效率原则;不需要大规模开发PPC程序,但需要时能写几段、调试时能看懂哪里错了。本文将从对比x86入手,引入RISC及PowerPC体系概念,向读者介绍该体系指令集,常用优化方法和交叉编译环境及模拟器的搭建等内容。

PowerPC基础知识
1990年 IBM时任总裁 Kuehler说服了摩托罗拉公司和苹果公司与IBM公司共同参与制订 PowerPC体系结构。为了让 AS/400也成为其中一员,1991 / 1992年罗彻斯特实验室开始为 AS/400扩充并制订PowerPC的64位结构。
                                                                                                         ----《罗彻斯特城堡》

大部分CPU指令集都可以分为:数据读写、数值计算、流程控制与设备管理四个部分,其中设备管理不属于介绍范围。开放系统软件标准在于硬件/软件只要符合该标准都能在 PowerPC下运行,也就是说先今有大量CPU虽然实现不一,但是他们在标准上都支持了 PowerPC体系,使得开发与接口更为方便。
PPC使用RISC(精简指令集),指令字长都是32bit,一条Intel指令往往可以由多条 PPC指令组合表示。Endian一般都是可调的,默认使用BE(Big Endian),同时PPC没有栈,也就是说应用程序需要自己实现相关操作。
常用术语介绍


常用寄存器
问题1:如何加载32位立即数?
在PPC下如何加载32位的立即数呢?RISC下PPC的每条指令都是4个字节定长。除去指令与寄存器参数编码,只有剩下16bit的长度用来描述立即数,比如立即数加载指令 LI:
LI rD, SIMM

立即数SIMM字段仅16位,如何表示32位?
答案:只有分两次载如,使用LIS(立即数载入并左移)和ADDI(立即数加法)分两次加载。因此32bit的立即数加载需要分两次完成:
LIS R3, 0x1122           加载并左移16位
ADDI R3, R3, 0x3344  再加上低16位
两条指令后,R3完成对 0x11223344的加载
特性:不一样的子程序调用
•    f1:             子程序入口
•       blr          返回(跳转到LR地址)
•    start:
•       bl f1       调用f1(跳转并保存地址到LR)
•       li r1, 1    设置r1 = 1
•       li r3, 1    设置r3 = 1
•       sc           系统调用:结束程序

PPC使用了LR寄存器(Link Register)来完成:在bl指令跳转前,下条指令(li r1,1)的地址会被保存到LR而执行到f1中的blr时,系统会跳转到LR所表示的地址,完成返回。
数据读写指令

注意:LBZ R3, 0(R2)与LHZ R3,10(R2)并不全等同于MOV AL,[EBX]和MOV AX,[EBX+10]。前者将字节和半字加载到R3时顺便清空了高位,而后两条指令加载数据到EAX并不会清空高位。
第一个程序:Hello World !!
把下面的程序保存成 hello.s,并交叉编译:
# powerpc-eabi-as -gstabs hello.s -o hello.o
# powerpc-eabi-ld hello.o -o hello
•    .global _start                        /* 请将本程序保存成 hello.s */
•    .data                                     /* 后面将讲解如何在虚拟机中调试 */
•    msg: .asciz "Hello, PowerPC World !!/n"
•    len = . - msg
•    .text                                       /* 代码部分开始 */
•    _start:
•        li %r0, 4                            /* r0 = 4   */
•        li %r3, 1                            /* r3 = 1   */
•        lis %r4, msg@ha               /* r4 = msg(high) << 16 */
•        addi %r4, %r4, msg@l       /* r4 = r4 + msg(low)     */
•        li %r5, len                         /* r5 = len */
•        sc                                     /* system call (print) */
•        li %r0, 1                           /* r0 = 1 */
•        li %r3, 1                           /* r3 = 1 */
•        sc                                     /* system call (exit) */

完成交叉编译后用 qemu模拟器执行:
# qemu-ppc hello
Hello, PowerPC World !!
关于如何在x86环境下交叉编译与调试,详细见第三部分的的“PowerPC编译调试”。
特殊寄存器操作
问题2:没有栈仅靠LR如何递归?
•    f1:               
•       mflr r2                     保存LR中记录的地址到r2
•       stw r2, -8(r1)          记录r2的数值到MEM[r1-8]处
•       addi r1, r1, -60        r1后移60个字节,完成进栈操作
•       ….
•       addi r1, r1, 60        r1前移60个字节,准备出栈
•       lwz r2, -8(r1)          读出老的LR值到r2
•       mtfr r2                    将r2的内容复制到LR
•       blr                           返回(跳转到LR地址)
•    start:
•       ….

虽然PPC没有直接提供栈相关指令(PUSH/POP/CALL/RET),应用程序却常用R1来模拟栈指针,实现多层调用时对LR的记录与恢复。
返回列表