有没有办法使用gcc将C转换为MIPS?

时间:2010-11-14 00:13:13

标签: c gcc compiler-construction mips

我为一个类完成了C到MIPS的转换,我想针对程序集进行检查。我听说有一种配置gcc的方法,它可以将C代码转换为MIPS架构而不是x86架构(我的计算机用户使用Intel i5处理器)并打印输出。

在Ubuntu(gcc附带)中运行终端,我用什么命令配置gcc转换为MIPS?我还需要安装什么吗?

编辑: 让我澄清一下。请阅读这个。 我不是在寻找使用哪种编译器,或者人们说“你可以交叉编译,但你应该使用其他没有指示如何设置的东西。”

如果您要发布,至少请参阅说明。 GCC附带Ubuntu。我没有关于如何安装编译器的经验,除了GCC之外,找不到其他任何内容的在线教程并不容易。然后是我需要知道的交叉编译的情况。谢谢。

7 个答案:

答案 0 :(得分:26)

GCC 可以为大量架构生成汇编代码,包括MIPS。但是,当GCC本身被编译时,决定了给定GCC实例所针对的架构。您将在Ubuntu系统中找到的预编译二进制文件知道x86(可能是32位和64位模式),但不知道MIPS。

使用与GCC本身将运行的体系结构不同的目标体系结构编译GCC称为准备交叉编译工具链。这是可行的,但需要相当多的文档阅读和耐心;您通常需要首先构建交叉汇编器和交叉链接器(GNU binutils),然后构建交叉GCC本身。

我建议使用buildroot。这是一组脚本和makefile,旨在帮助生成完整的交叉编译工具链和实用程序。在一天结束时,您将获得目标系统的完整操作系统和开发工具。这包括您所追求的交叉编译器。

另一个完全不同的解决方案是使用QEMU。这是用于各种处理器和系统的仿真器,包括MIPS系统。您可以使用它来运行具有MIPS处理器的虚拟机,并在该计算机内安装MIPS操作系统,例如: Debian,Linux发行版。这样,您就可以获得 native GCC(在MIPS系统上运行的GCC并为MIPS生成代码)。

QEMU方式可能更简单;使用交叉编译需要对一些毛茸茸的细节有所了解。无论哪种方式,您都需要大约1 GB的可用磁盘空间。

答案 1 :(得分:5)

这不是配置,你需要一个交叉编译为MIPS的GCC版本。这需要一个特殊的GCC构建,并且相当毛茸茸的设置(构建GCC不适合胆小的人)。

我建议您使用LCC。使用LCC进行交叉编译比使用GCC更容易,并且在当前机器上构建LCC只需几秒钟。

答案 2 :(得分:2)

您需要将源代码下载到binutils和gcc-core,并使用类似../configure --target=mips ...的内容进行编译。您可能需要选择特定的MIPS目标。然后你可以使用mips-gcc -S

答案 3 :(得分:1)

您可以交叉编译GCC,以便生成MIPS代码而不是x86。这是一个很好的学习经历。

如果您想要快速获得结果,您还可以获得具有MIPS支持的预构建GCC。一个是CodeSourcery Lite工具链。它是免费的,适用于许多架构(包括MIPS),他们已经准备好为Linux和Windows使用二进制文件。

http://www.codesourcery.com/sgpp/lite/mips/portal/subscription?@template=lite

答案 4 :(得分:1)

您应该从Ubuntu存储库中安装交叉编译器。存储库中提供了四个GCC MIPS C交叉编译器。根据您的需要选择:

  • gcc-mips-linux-gnu-32位big-endian。
  • gcc-mipsel-linux-gnu-32位little-endian。
  • gcc-mips64-linux-gnuabi64-64位big-endian。
  • gcc-mips64el-linux-gnuabi64-64位little-endian。

(供Debian用户使用的注意事项:如果您通常希望使用build-essential软件包安装常规编译器,那么您将有兴趣了解crossbuild-essential-mipscrossbuild-essential-mipsel和{ {1}})

在以下示例中,我将假定您选择了32位的低字节序版本(crossbuild-essential-mips64el)。其他MIPS版本的命令与此类似。

要处理MIPS而不是系统的本机体系结构,请使用sudo apt-get install gcc-mipsel-linux-gnu命令而不是mipsel-linux-gnu-gcc。例如,gcc生成包含MIPS程序集的文件mipsel-linux-gnu-gcc -fverbose-asm -S myprog.c

查看MIPS程序集的另一种方法:运行myprog.s以生成包含调试信息的目标文件mipsel-linux-gnu-gcc -g -c myprog.c。然后使用myprog.o查看目标文件的反汇编。例如,如果mipsel-linux-gnu-objdump -d -S myprog.o是这样的:

myprog.c

如果使用#include <stdio.h> int main() { int a = 1; int b = 2; printf("The answer is: %d\n", a + b); return 0; } 进行编译,则mipsel-linux-gnu-gcc -g -c myprog.c将显示如下内容:

mipsel-linux-gnu-objdump -d -S myprog.o

答案 5 :(得分:0)

你应该编译自己的gcc版本,它能够交叉编译。当然这并不容易,因此您可以寻找不同的方法......例如this SDK。

答案 6 :(得分:0)

一次性使用一个小程序或几个函数,不需要在本地安装任何东西。

使用Matt Godbolt的编译器浏览器站点https://godbolt.org/,该站点具有针对各种ISA(包括MIPS和x86-64)和一些其他编译器的GCC和clang。

请注意,默认情况下,编译器资源管理器会过滤指令,以便您可以 just 查看指令,而忽略诸如对齐,节,.globl等内容。 (对于没有全局/静态数据的函数,这实际上很好,尤其是当您只想使用编译器为您创建示例时。无论如何,如果您不使用任何代码,则默认部分为.text指令。


大多数需要MIPS asm做作业的人正在使用SPIM或MARS ,通常没有分支延迟插槽。 (与真正的MIPS不同,因此您需要调整编译器,以使可以在分支无条件运行后利用下一条指令,即使使用了该指令也是如此。)对于GCC,该选项为-fno-delayed-branch -它将用NOP填充每个延迟时隙,因此代码仍将在真实的MIPS上运行。您可以手动删除所有NOP。

可能还需要进行其他调整,例如MARS可能要求您使用jr $31而不是j $31Tweak mips-gcc output to work with MARS。当然,必须使用MARS's toy system calls来实现I / O代码,而不是对jalprintf之类的标准库函数进行std::ostream::operator<<调用。不过,您可以有用地编译(和手动调整)asm来处理数据,例如将整数相乘或对数组求和或反转。

不幸的是,GCC不能选择使用$a0之类的寄存器名来代替$r。对于PowerPC,-mregnames使用r1而不是1,但是MIPS没有类似的选项使用“更多符号”注册表名称。

int maybe_square(int num) {
    if (num>0)
        return num;
    return num * num;
}

On Godbolt 和GCC 5.4 -xc -O3 -march=mips32r2 -Wall -fverbose-asm -fno-delayed-branch

-xc编译为C,而不是C ++,因为我发现这比在下拉列表中的C和C ++语言之间切换和让站点清除我的源代码更方便。

-fverbose-asm用目标和源的C变量名称注释汇编。 (在优化的代码中,这通常是临时的,但并非总是如此。)

-O3启用了全面优化,因为默认的-O0调试模式对人类来说是一个可怕的混乱。如果您想手工查看代码并查看其如何实现源代码,请始终至少使用-OgHow to remove "noise" from GCC/clang assembly output?。如果使用SIMD指令为ISA进行编译,也可以使用-fno-unroll-loops-fno-tree-vectorize

这使用mul而不是经典的MIPS mult + mflo,这要归功于-march=选项告诉GCC我们正在编译更高版本的MIPS ISA,而不是不管默认基线是多少。 (也许是MIPS I,又名R2000,-march=mips1

另请参阅MIPS target options上GCC手册的部分。

# gcc 5.4 -O3
square:
        blez    $4,$L5
        nop
        move    $2,$4    # D.1492, num         # retval = num
        j       $31                            # jr $ra  = return
        nop

$L5:
        mul     $2,$4,$4   # D.1492, num, num   # retval = num * num
        j       $31                             # jr $ra  = return
        nop

或者使用clang,使用-target mips告诉它针对MIPS进行编译。 您可以在桌面上执行此操作;与GCC不同,clang通常是在启用了多个后端的情况下构建的。

从相同的Godbolt链接中,发出铛10.1 -xc -O3 -target mips -Wall -fverbose-asm -fomit-frame-pointer。默认目标显然是MIPS32或类似clang的目标。另外,clang默认情况下会为MIPS启用帧指针,从而使asm嘈杂。

请注意,它选择制作无分支的asm,将if转换为条件移动,以在原始输入和mul结果之间进行选择。不幸的是clang不支持-fno-delayed-branch;也许它为相同的选项取了另一个名字,或者也许没有希望。

maybe_square:
        slti    $1, $4, 1
        addiu   $2, $zero, 1
        movn    $2, $4, $1            # conditional move based on $1
        jr      $ra
        mul     $2, $2, $4            # in the branch delay slot

在这种情况下,我们可以简单地将mul放在jr之前,但是在其他情况下,转换为无分支延迟的asm并非完全不重要。例如在递减计数器 之前在循环计数器上跳转,不能通过将递减放在第一位来撤消它;那会改变意思。


注册名:

编译器使用寄存器号,而不用打扰名字。为了供人类使用,您通常会希望将其翻译回来。在线上的许多地方都有MIPS寄存器表,这些表显示$ 4 .. $ 7是$ a0 .. $ a3,$ 8 .. $ 15是$ t0 .. $ t7等。例如this one