【问题标题】:Choosing CPU architecture for LLVM/CLANG为 LLVM/CLANG 选择 CPU 架构
【发布时间】:2011-09-20 11:38:16
【问题描述】:

我正在设计 TTL 串行计算机,我正在努力选择更适合 LLVM 编译器后端的架构(我希望能够在那里运行任何 C++ 软件)。不会有 MMU、没有乘法/除法、没有硬件堆栈、没有中断。

我有两个主要选择:

1) 8 位内存、8 位 ALU、8 位寄存器 (~12-16)。内存地址宽度 24 位。所以我需要使用 3 个寄存器作为 IP,3 个寄存器用于任何内存位置。

不用说,任何地址计算在编译器中实现都是非常痛苦的。

2) 24 位内存、24 位 ALU、24 位寄存器 (~6-8)。平坦的记忆,不错。缺点是由于设计的串行性质,即使我们在一些布尔值上进行操作,每个操作也需要多花 3 倍的时钟。 24 位内存数据宽度很昂贵。而且一般来说在硬件上实现起来也比较困难。


问题是:您认为在这个基于 8 位、无堆栈的硬件上实现所有 c++ 功能是可能的,还是我需要更复杂的硬件来生成质量和速度合理的代码?

【问题讨论】:

  • 这可能是一个幼稚的问题,但为什么你必须实现 C++ 的所有特性呢?就不能用写一个新的 LLVM 目标架构,Clang 编译 C++ 就没有问题了吗?
  • @Dan Cecile 这正是我要做的。但是您可以看到,为具有 24 位内存空间的 8 位 CPU 编写 LLVM 后端可能有点不简单。
  • 所以你有 4 个部分;弄清楚如何实现LLVM assembly language 与您的硬件一起使用,复制和调整existing LLVM target 以与您的硬件一起使用,弄清楚如何让Clang 到generate 8-bit LLVM bytecode (pointer sizes are configurable),然后将您的硬件更改为更多合适。

标签: hardware cpu llvm clang


【解决方案1】:

我同意使用 LCC 的建议。我在这个自制的 16 位 RISC 项目中使用了它:http://fpgacpu.org/xsoc/cc.html

我认为无论您构建 8 位变体并使用 3 个 add-with-carry 来增加 IP,还是构建 24 位变体并在硬件中完成整个事情,这应该不会有太大区别。您可以在汇编程序中隐藏差异。

如果您查看我上面的文章,或者这里更简单的 CPU:http://fpgacpu.org/papers/soc-gr0040-paper.pdf,您会发现您确实不需要那么多运算符/指令来涵盖整数 C 重复。事实上,有一个 lcc 实用程序 (ops) 可以打印给定机器的 min 运算符集。

有关更多信息,请参阅我关于将 lcc 移植到新机器的文章:http://www.fpgacpu.org/usenet/lcc.html

移植 lcc 后,我编写了一个汇编程序,它从基本指令中合成了更大的指令集。例如,我的机器有 load-byte-unsigned 但没有 load-byte-signed,所以我发出了这个序列:

lbs rd,imm(rs) ->
  lbu rd,imm(rs)
  lea r1,0x80
  xor rd,r1
  sub rd,r1

所以我认为你可以通过这个最小的操作封面:

  registers
  load register with constant
  load rd = *rs
  store *rs1 = rs2
  + - (w/ w/o carry)    // actually can to + with - and ^
  >> 1                  // << 1 is just +
  & ^                   // (synthesize ~ from ^, | from & and ^)
  jump-and-link rd,rs   // rd = pc, pc = rs
  skip-z/nz/n/nn rs     // skip next insn on rs==0, !=0, <0, >=0

更简单的是没有寄存器(或等效地用内存模糊寄存器——所有寄存器都有一个内存地址)。

为 SP 预留一个寄存器,并在编译器中编写函数 prolog/epilog 处理程序,您不必担心堆栈指令。只需要存储每个被调用方保存寄存器、根据帧大小调整 SP 等的代码。

中断(以及从中断返回)很简单。您需要做的就是将跳转和链接指令强制到指令寄存器中。如果您选择位模式为 0,并将正确的地址放入源寄存器 rs(特别是如果它是 r0),则可以通过触发器复位输入或额外的 force-to- 0 和门。我在上面的第二篇论文中使用了类似的技巧。

有趣的项目。我看到一场 TTL / 7400 竞赛正在进行中,我在想一台机器有多简单,你可以逃脱,向机器添加一个 32 KB 或 128 KB 异步 SRAM 来保存代码和数据会不会是作弊。

无论如何,愉快的黑客攻击!

附言

1) 您需要确定每个整数类型的大小。如果您愿意,您当然可以使 char、short、int、long、long long 等大小相同,一个 24b 字,尽管它不符合最小表示范围。

2) 虽然我在这里专注于 lcc,但您问的是 C++。我建议先说服 C。一旦你弄清楚了 C 的东西,包括软件中的 *、/、% 运算符等,无论是在 LLVM 还是 GCC 中,转向成熟的 C++ 应该更容易处理。 C 和 C++ 之间的区别“仅”是处理虚函数调用、指向成员解引用的指针、动态转换、静态构造函数、异常所需的额外 vtable 和 RTTI 表和代码序列(完全构建了原始 C 整数运算符重复)处理等。

【讨论】:

  • 附注我刚在想。您应该优先构建 24b 变体而不是 8b 变体。这并不是因为更广泛的数据操作每个都需要多条指令,而是因为您确实需要一个用于指令和数据的线性 24b 地址空间的概念。虽然您可以从 3 个 8b 负载中合成 24b 负载,但您需要一个 24b 地址(和一个 24b 地址增量)来处理硬件中的指令提取、程序计数器和 PC 增量。如果您只有 8b 个地址,尚不清楚如何处理大于 256 条指令的程序。
【解决方案2】:

恕我直言,c 编译器是可能的。不过,我不确定 C++。

LLVM/CLang 可能是 8 位计算机的艰难选择,

相反,首先尝试lcc,然后再尝试 llvm/etc、HTH。

Bill Buzbee 成功地为他的 Magic-1(称为 homebrewcpu)重新定位 lcc 编译器。

虽然 Magic-1 的硬件设计和构造通常最受关注,但该项目的最大部分(到目前为止)一直是开发/移植软件。为此,我不得不从头开始编写一个汇编器和链接器,重新定位一个 C 编译器,编写和移植标准 C 库,编写一个简化的操作系统,然后移植一个更复杂的操作系统。这是一个挑战,但也很有趣。我想我有点扭曲,但我碰巧喜欢调试困难的问题。而且,当您试图追查的错误可能涉及以下一项或多项时:硬件设计缺陷、电线松动或断线、TTL 芯片松动或损坏、汇编器错误、链接器错误、编译器错误、C 运行时库错误或最终有问题的程序中的错误有很多有趣的机会。哦,我也懒得将这些错误归咎于其他人。

我一直惊讶于这该死的东西竟然能跑,更不用说跑得这么好了。

【讨论】:

    【解决方案3】:

    在我看来,无堆栈硬件已经不太适合 C 和 C++ 代码。如果你有嵌套的函数调用,无论如何你都需要在软件中模拟一个堆栈,这当然要慢得多。

    当采用无堆栈路线时,您可能会将大部分变量分配为“静态”,并且没有可重入函数。在这种情况下,6502 风格的寻址模式可能是有效的。例如,您可以使用以下寻址模式:

    1. 立即地址(24 位)作为操作码的一部分
    2. 立即地址(24 位)加索引寄存器(8 位)
    3. 间接访问:直接到内存的 24 位地址,其中包含实际地址
    4. 间接访问:24 位地址到内存,8 位索引寄存器添加到内存中的值。

    上述地址模式将允许有效访问分配在恒定地址(静态分配)的数组、结构和对象。对于动态和堆栈分配的对象,它们的效率较低(但仍然可用)。

    您还可以从串行设计中获得一些好处:通常 24 位 + 8 位加法不需要 24 个周期,但您可以在进位为 0 时将加法短路。

    除了直接将 IP 映射为寄存器外,您还可以只允许通过 goto/branch 指令更改它,使用与上述相同的地址模式。跳转到动态计算的地址非常少见,因此直接在操作码中给出整个 24 位地址更有意义。

    我认为,如果您仔细设计 CPU,您可以非常有效地使用许多 C++ 功能。但是,不要期望任何随机的 C++ 代码都能在如此有限的 CPU 上快速运行。

    【讨论】:

    • 是的,无堆栈代码只是无用的:我仍然希望能够交叉编译现有代码。所以我将不得不使用模拟堆栈。
    • 你可以实现一个硬件栈;它只需要多两条指令,并且在串行架构中递增/递减非常快速和容易。
    【解决方案4】:

    实现当然是可能的,但我怀疑它是否可用(至少对于 C++ 代码)。正如已经指出的,第一个问题是缺少堆栈。其次,一堆C++严重依赖动态内存分配,C++“内部”结构也很大。

    所以,在我看来,如果你这样做会更好:

    1. 摆脱 C++ 要求(或至少,将自己限制在某个子集)
    2. 对所有内容都使用 24 位,而不是 8 位(对于寄存器也是如此)
    3. 添加硬件堆栈

    【讨论】:

    • 嗯,实现子集比整个 C++ 更难。在后一种情况下,我只需要编写 clang/llvm 后端。为什么缺少硬件堆栈这样一个关键问题?软件不允许做同样的事情吗?
    • 如果你不关心任何性能 - 那么你很好,是的:)
    【解决方案5】:

    您将无法在那里运行“任何”C++ 代码。例如 fork()、system() 等。任何明显依赖于中断的东西。你可以走很长的路,当然。 现在您的意思是任何可以/已经用 C++ 编写的程序,还是您仅将自己限制在该语言而不是通常与 C/C++ 相关联的库?语言本身是一个更容易接受的规则。

    我认为更简单的问题/答案是,为什么不尝试一下?你试过什么了?可以说 x86 是一台 8 位机器,不考虑对齐和许多 8 位指令。 msp430 被移植到 llvm 以显示它是多么容易和快速地完成,我希望看到该平台有更好的支持(不是我的优势所在,否则我会这样做)一个 16 位平台。没有mmu。确实有堆栈和中断,不必使用它们,如果您删除库规则,那么剩下什么需要中断?

    我会查看 llvm,但请注意,生成的文档显示了移植的容易程度、过时且错误,您基本上必须从编译器源代码中自行弄清楚。 llc 有一本书,以它而闻名,但没有经过优化。资源在现代计算机上不能很好地编译,总是不得不及时倒退才能使用它,任何时候我在一个晚上后试图构建它时靠近它,我就放弃了。 vbcc,简单,干净,有文档,对较小的处理器不友好。是C++吗,不记得了。在所有这些中,最容易启动并运行编译器。在所有这些中,总而言之,LLVM 是最有吸引力和最有用的。不要靠近 gcc,甚至不要去想它,管道胶带和里面的钢丝绳把它固定在一起。

    你发明了你的指令集了吗?你有模拟器和汇编器吗?在 github 上查找 lsasim 以找到我的指令集。你可以为我写一个 llvm 后端作为你的练习...咧嘴笑...(我的 vbcc 后端很糟糕,我需要重新开始)...

    您必须对高级别的实现方式有所了解,但您确实必须从指令集、指令集模拟器和某种汇编程序开始。然后开始手动将 C/C++ 代码转换为您的指令集的程序集,这应该很快让您完成“我可以在没有堆栈的情况下执行此操作吗”等。在此过程中定义您的调用约定,手动实现更多 C/C++ 代码使用您的调用约定。然后深入研究编译器并制作后端。我认为您应该将 vbcc 视为垫脚石,然后如果它看起来像 LLVM(ISA)可以工作,则前往 LLVM。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-06
      • 1970-01-01
      • 2013-04-29
      • 1970-01-01
      • 2021-06-17
      • 2020-05-10
      • 1970-01-01
      相关资源
      最近更新 更多