我有一个项目,在ARM Cortex-M4处理器上运行,我正在尝试包含gcc链接时优化(LTO)功能.
目前我的编译和链接标志是:
CFLAGS = -ggdb -ffunction-sections -Og
LDFLAGS = -Wl,-gc-sections
Run Code Online (Sandbox Code Playgroud)
使用这些标志一切正常,我能够正确调试项目.
然后我尝试添加-flto到CFLAGS.虽然程序工作正常,但我无法再调试项目,gdb抱怨缺少调试符号.objdump -g在ELF文件上运行(启用LTO)会提供以下输出:
xxx.elf: file format elf32-littlearm
Contents of the .debug_frame section:
00000000 0000000c ffffffff CIE
Version: 1
Augmentation: ""
Code alignment factor: 2
Data alignment factor: -4
Return address column: 14
DW_CFA_def_cfa: r13 ofs 0
00000010 00000018 00000000 FDE cie=00000000 pc=08002a3c..08002a88
DW_CFA_advance_loc: 2 to 08002a3e
DW_CFA_def_cfa_offset: 16
DW_CFA_offset: r4 at cfa-16
DW_CFA_offset: r5 at cfa-12
DW_CFA_offset: r6 at cfa-8
DW_CFA_offset: r14 at cfa-4
DW_CFA_nop …Run Code Online (Sandbox Code Playgroud) 我正在将一些 AArch64/ARM64/Apple Silicon 汇编代码从 Linux 移植到 macOS。
\n此代码使用所有 31 个可用寄存器(堆栈指针不算在内)来避免几乎所有溢出情况;Linux 调用约定允许我使用那么多寄存器。
\n如果迫不得已,我承认溢出一个额外的寄存器(从而将其减少到使用 30 个寄存器)是可行的,因为性能受到的影响最小,但如果限制为 29 个或更少的可用寄存器,性能将受到更大的影响。因此,我真的希望至少有 30 个可用寄存器,最好是 31 个。
\n我刚刚从 Apple 官方文档中了解到,保留了两个额外的寄存器,超出了 Linux 调用约定的要求:
\n\n\n尊重特定 CPU 寄存器的用途
\nARM 标准将某些决策委托给平台设计者。Apple 平台遵循以下选择:
\n\n
\n- 该平台保留寄存器x18。不要\xe2\x80\x99 使用该寄存器。
\n- 帧指针寄存器 (x29) 必须始终寻址有效的帧记录。某些函数 \xe2\x80\x94 例如叶函数或尾部调用 \xe2\x80\x94 可能会选择不在该列表中创建条目。因此,即使没有调试信息,堆栈跟踪也始终是有意义的。
\n
尽管有这些说法,我的代码在没有它的情况下似乎运行良好。
\n现在,我完全明白忽略此类 ABI 要求是一件非常糟糕的事情 (TM)。但是,我想确切地了解代码如何因使用 x18 和 x29 而中断。
\n例如,通过阅读上述文档,我的理解是 x29 是为了支持调试或故障转储。假设我不关心调试这个函数(实际上我不关心),或者任何生成的故障转储是否有意义。那么,使用x29有什么坏处吗?
\n至于x18,知道它有什么用吗?我假设(零支持证据)如果在该代码运行时执行中断或上下文切换,则不会保存 x18,因此一旦返回就会破坏我的函数的结果。这将是一个更严重的情况,我会听取建议,在这种情况下不要使用 x18。
\n另请注意,所讨论的代码是叶函数,因此破坏从其中调用的任何函数都没有问题。
\n假设我有两个uint16_t[4]数组,a并且b. 这些数组中的每个整数都在 [0, 16383] 范围内,因此未设置第 14 和 15 位。a[i]然后我有一些代码来查找每个b[i]之间的最小值和最大值i:
uint16_t min[4], max[4];
for (int i = 0; i < 4; i++) {
if (a[i] < b[i]) {
min[i] = a[i];
max[i] = b[i];
} else {
min[i] = b[i];
max[i] = a[i];
}
}
Run Code Online (Sandbox Code Playgroud)
假设由于某种原因我不能/不会使用 SIMD,但我仍然想在 64 位平台上尽可能快地计算它。因此,一个自然的解决方案是在 64 位寄存器上使用 SIMD 寄存器内 (SWAR) 范例,在单次迭代中计算这 4 个值,而不是使用 16 位算术进行超过 4 次迭代。
可以使用哪些位操作技巧来使用 SWAR 范例来实现(最小或最大)或理想情况下这两种操作,以便生成的代码比上面的循环更快?我的目标架构是 ARMv8,因此请随意使用任何有助于减少指令数量的 ARMv8 指令。
C、汇编或 C+ …
我使用 Visual Studio Code 和 Microsoft 的 C/C++ 官方扩展来编写 C 程序,在 macOS Catalina 10.15.3 下运行。项目文件夹位于我的主目录中 Documents 文件夹内的路径中。
每次我尝试调试我的代码时,都会弹出一个带有消息的确认窗口"<program>" would like to access files in your Documents folder。我必须单击确定按钮才能继续。
这减慢了我的工作流程,并使系统偏好设置 => 安全和隐私 => 隐私 => 文件和文件夹中的允许应用程序列表变得混乱(奇怪的是,它不允许我从列表中删除文件——我会稍后在 Ask Different 中提出有关此问题的问题,但这不是主要问题:我想完全摆脱此确认窗口。)
有没有办法直接进入调试会话,而不必每次都单击此消息上的“确定”?
这里有一个问题,在C / C ++扩展的GitHub的问题跟踪显然对同样的问题,建议的解决方案是授予访问终端。但是,这并不能解决我的问题——事实上,自从我每天使用终端以来,很久以前就已经授予终端访问 Documents 文件夹的权限。
我正在为 Cortex-M4 编写一些汇编代码,特别是 STM32F4DISCOVERY 套件中的 STM32F407VG。
该代码对性能极其敏感,因此我希望从中挤出最后一个周期。我对其进行了基准测试(使用 Cortex-M4 中提供的 DWT 周期计数器),对于特定大小的输入,它以 1494 个周期运行。代码从闪存运行,CPU 降频至 24 MHz,以确保对闪存进行真正的零等待状态访问(禁用 ART 加速器)。对 DWT 周期计数器的两次连续读取进行基准测试会产生一个周期,因此这是与基准测试相关的唯一开销。
该代码仅从闪存读取 5 个常量 32 位字(如果从闪存读取指令和数据,可能会导致总线矩阵争用);所有其他数据存储器访问都是从 RAM 进行或到 RAM 进行的。我确保所有分支目标都是 32 位对齐的,并手动.W向某些指令添加后缀以消除所有指令,除了两个 16 位但不是 32 位对齐的 32 位指令——其中之一没有即使运行这个输入大小,第二条是POP函数的最终指令,它显然不是在循环中运行。请注意,大多数指令使用 32 位编码:实际上,平均指令长度为 3.74 字节。
我还制作了一个电子表格,记录了代码中的每一条指令,它们在循环内运行了多少次,甚至还记录了每个分支是否被采用,因为这会影响给定指令需要的周期数。我阅读了Cortex-M4 技术参考手册(TRM) 来获取每条指令的周期计数,并始终使用最保守的估计:当一条指令取决于管道刷新的成本时,我假设它最多需要 3 个周期;此外,我假设了所有加载和存储的最坏情况,尽管 TRM 第 3.3.2 节中讨论的许多特殊情况实际上可能会减少这些计数。我的电子表格包含 DWT 周期计数器两次读取之间每条指令的成本。
因此,我非常惊讶地发现我的电子表格预测代码应该在 1268 个周期内运行(回想一下实际性能是 1494 个周期)。我无法解释为什么代码的运行速度比根据指令时序假定的最坏情况慢 18%。即使完全展开代码的主循环(应负责大约 3/4 的执行时间)也只能将其减少到 1429 个周期,并且快速调整电子表格表明此展开的版本应在 1186 个周期内运行。
有趣的是,同一算法的完全展开、仔细调整的 C 版本运行了 1309 个周期。它总共有 1013 条指令,而我的汇编代码的完全展开版本有 930 条指令。在这两种情况下,都有一些代码处理未由用于基准测试的特定输入执行的情况,但就这些未使用的代码而言,C 版本和汇编版本之间应该没有显着差异。最后,C …
考虑以下在 ARM Cortex-A72 处理器上运行的代码(此处为优化指南)。我已经包含了我期望的每个执行端口的资源压力:
| 操作说明 | 乙 | Ⅰ0 | Ⅰ1 | 中号 | L | S | F0 | F1 |
|---|---|---|---|---|---|---|---|---|
.LBB0_1: |
||||||||
ldr q3, [x1], #16 |
0.5 | 0.5 | 1 | |||||
ldr q4, [x2], #16 |
0.5 | 0.5 | 1 | |||||
add x8, x8, #4 |
0.5 | 0.5 | ||||||
cmp x8, #508 |
0.5 | 0.5 | ||||||
mul v5.4s, v3.4s, v4.4s |
2 | |||||||
mul v5.4s, v5.4s, v0.4s |
2 | |||||||
smull v6.2d, v5.2s, v1.2s |
1 | |||||||
smull2 v5.2d, v5.4s, v2.4s |
1 | |||||||
smlal v6.2d, v3.2s, v4.2s |
1 | |||||||
smlal2 v5.2d, v3.4s, v4.4s |
1 | |||||||
uzp2 v3.4s, v6.4s, v5.4s |
1 | |||||||
str q3, [x0], #16 … |
考虑以下代码(编译器资源管理器链接),在 gcc 和 clang 下编译并进行-O3优化:
#include <arm_neon.h>
void bug(int8_t *out, const int8_t *in) {
for (int i = 0; i < 2; i++) {
int8x16x4_t x;
x.val[0] = vld1q_s8(&in[16 * i]);
x.val[1] = x.val[2] = x.val[3] = vshrq_n_s8(x.val[0], 7);
vst4q_s8(&out[64 * i], x);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:这是一个问题的最小可重现版本,该问题出现在我实际的、更复杂的代码的许多不同函数中,其中充满了执行与上面完全不同的操作的算术/逻辑/排列指令。请不要批评和/或建议执行上述代码的不同方法,除非它对下面讨论的代码生成问题有影响。
clang 生成正常的代码:
bug(signed char*, signed char const*): // @bug(signed char*, signed char const*)
ldr q0, [x1]
sshr v1.16b, v0.16b, #7
mov v2.16b, v1.16b
mov v3.16b, v1.16b
st4 { v0.16b, …Run Code Online (Sandbox Code Playgroud) 我有一个基于ST Microelectronics STM32F103VE微控制器的定制板,MiniSD卡插在微控制器的SDIO总线上.电气连接完全按照STM3210E-EVAL电路板原理图进行,检查并重新检查,因此我非常有信心它们是正确的.不幸的是,我没有那个评估板来测试我遇到的是硬件问题,但似乎并非如此.对于下面的测试,我使用的是最近购买的金士顿2 GB MicroSD卡(型号MBLYG2/2GB)(因此它应符合最新的SD卡规格)和随附的MicroSD到MiniSD适配器; 尚未测试任何其他卡.
我正在遵循SD卡物理层简化规范来了解正在发生的事情.我正在使用的代码是ST Micro为该微控制器提供的标准外设库附带的示例SDIO代码.它首先发送CMD0(GO_IDLE_STATE),然后发送CMD8(SEND_IF_COND),然后发送ACMD41(SD_SEND_OP_COND),这是通过发送CMD55(APP_CMD)然后发送CMD41来完成的.时钟线的时钟频率为400 kHz,作为调试工作的一部分,我在CMD0和CMD8之间增加了大约100个时钟周期的延迟,因为我已经读到了需要的地方,至少在SPI中运行时模式.除了下面提到的修改之外,代码与示例代码完全相同.
当我第一次尝试运行示例代码时,CMD55出现问题,原因是微控制器正在缓冲对CMD8的响应,但示例代码没有检索到CMD8的响应,因此在检查对CMD55的响应时,代码是实际上看着对CMD8的反应,并对此感到不安.我通过在发送CMD55之前清除微控制器的SDIO外设上的CMDREND标志来解决这个问题,因此当代码检查了对CMD55的响应时,它不再缓冲CMD8的响应.
下一个问题,也就是我当前坚持的问题是,在对CMD55的响应中,卡状态字段(COM_CRC_ERROR)的第23位被置位,这表示根据规范,前一命令的CRC校验失败.虽然微控制器自动计算CRC,但我将逻辑分析仪连接到电路以验证它是否正确.我正在使用网络应用程序(抱歉,无法链接,因为我是新用户,但只是Google用于"CRC ghsi"并且这是第一个结果)验证CRC,使用多项式x ^ 7 + x ^根据规格3 + 1.这是逻辑分析仪输出,由我格式化和评论:
// uC sends CMD0, CRC OK, no response:
01 000000 00000000000000000000000000000000 1001010 1
// uC sends CMD8, CRC OK, check byte = 0xAA:
01 001000 00000000000000000000000110101010 1000011 1
// SD card responds to CMD8, CRC OK, check byte echoed back = 0xAA:
00 001000 00000000000000000000000110101010 0001001 1
// uC sends CMD55, CRC OK:
01 110111 00000000000000000000000000000000 0110010 1
// …Run Code Online (Sandbox Code Playgroud) 在 Apple Silicon Mac 上运行以下 shell 脚本(在配备 M1 的 MacBook Air 2020 和配备 M3 Max 的 MacBook Pro 16" 上尝试;两者都运行 macOS Sonoma,配备 Apple clang 15.0):
#!/bin/bash
rm -rf NTRU
git clone https://github.com/vector-polymul-ntru-ntrup/NTRU.git
cd NTRU/ntruhps2048677/aarch64_tmvp
cat << EOF > speed_polymul.c
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "api.h"
#include "params.h"
#include "poly.h"
#include "tmvp.h"
#include "batch_multiplication.h"
#define NTESTS 1000
uint64_t time0, time1;
uint64_t cycles[NTESTS];
#ifdef __APPLE__
#include "m1cycles.h"
#define __AVERAGE__
#define SETUP_COUNTER() {(void)cycles; setup_rdtsc();}
#define CYCLE_TYPE "%lld"
#define …Run Code Online (Sandbox Code Playgroud) 根据此博客文章以及对此Stack Overflow问题的最高投票答案,该问题反过来引用了Apple的文档,在现代Swift中创建单例的最佳方法是:
class Singleton {
static let sharedInstance = Singleton()
}
Run Code Online (Sandbox Code Playgroud)
尽管未提及,但也可能需要使用a private init()。
对我来说,一种更简单的选择是将所有属性和方法都转换为static,然后删除该sharedInstance属性。
例如,假设我按照上述建议编写了一个带有属性和方法的类,如下所示:
class Singleton {
static let sharedInstance = Singleton("whatever")
var myProperty: String
func myMethod() {
// ...
}
private init(_ myProperty) {
self.myProperty = myProperty
}
}
Run Code Online (Sandbox Code Playgroud)
如果用户需要访问所讨论的属性,则可以编写Singleton.sharedInstance.myProperty;如果需要调用方法,则可以编写Singleton.sharedInstance.myMethod()。
我建议重写该类,如下所示:
class Singleton {
static var myProperty: String = "whatever"
static func myMethod() {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
因此:更少的样板代码,并且访问属性(just …
我正在 GNU 汇编器中编写针对 ARMv8 (AArch64) 的汇编代码。不确定这是否重要,但我直接在运行 Linux 的 ARMv8 目标(Raspberry Pi 板)中进行编码。
\n我有一个带有很长参数列表的宏(例如,超过 60 个参数)——我知道,这是错误的,请不要开枪打我。
\n为了便于阅读,我想将这个参数列表分成多行,即如下所示:
\n.macro my_macro arg1, arg2, arg3, arg4,\n arg5, arg6, arg7, arg8,\n // ...\n arg61, arg62, arg63, arg64\n\n // macro code goes here\n\n.endm\nRun Code Online (Sandbox Code Playgroud)\n我也想在实例化宏时执行相同的操作。
\n不幸的是,当我尝试在宏声明中插入换行符时,我收到以下错误:
\ntest.s: Assembler messages:\ntest.s:123: Error: Bad parameter list for macro `my_macro\'\nRun Code Online (Sandbox Code Playgroud)\n至于在宏实例化中插入换行符,虽然我没有直接收到错误,但代码不会汇编,因为换行符后面的所有参数都被忽略,即好像我用更少的参数实例化了宏比预期的要大,所以缺失的默认为空字符串。
\n根据手册:
\n\n\n可以使用 \xe2\x80\x98;\xe2\x80\x99 字符代替换行符来分隔语句。
\n
;我尝试在每行末尾添加一个,即:
.macro my_macro arg1, arg2, arg3, arg4, ;\n …Run Code Online (Sandbox Code Playgroud) 我在配备 Apple Silicon M1 CPU 的 MacBook Air 上使用 Homebrew 安装了 gcc 11.3.0。该二进制文件是 aarch64 本机版本,而不是 Rosetta 模拟版本。安装的操作系统是 macOS Monterey 12.3。
我在编译使用 ARMv8.2-A SHA-3 扩展指令(M1 CPU 支持)的程序时遇到问题。这是一个最小的可重现示例:
#include <arm_neon.h>
int main() {
uint64x2_t a = {0}, b = {0}, c = {0};
veor3q_u64(a, b, c);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这段代码可以用 Apple 提供的 clang 编译器很好地编译。
我使用 gcc 11 的以下命令行编译它:
gcc-11 -o test test.c -march=armv8-a+sha3
这会导致以下错误:
In file included from test.c:1:
test.c: In function 'main':
/opt/homebrew/Cellar/gcc/11.3.0/lib/gcc/11/gcc/aarch64-apple-darwin21/11/include/arm_neon.h:32320:1: error: inlining failed in call to 'always_inline' 'veor3q_u64': …Run Code Online (Sandbox Code Playgroud)