iOS 上 NEON 代码的奇怪结果,跳转到 __ARCLite__load?

Posted

技术标签:

【中文标题】iOS 上 NEON 代码的奇怪结果,跳转到 __ARCLite__load?【英文标题】:Strange results with NEON code on iOS, jump into __ARCLite__load? 【发布时间】:2013-07-02 00:24:30 【问题描述】:

我正在尝试编写一些 NEON 代码以优化 iPhone/iPad 上的单词数组填充。这个问题非常奇怪的是,当 NEON 指令为 q3 赋值时,代码似乎跳转到了名为 _ARCLite_load 的函数中。有没有人见过这样的东西:

(使用 xcode 4.6 和 -no-integrated-as 标志编译的 test_time_asm.s)

.section __TEXT,__text,regular
.section __TEXT,__textcoal_nt,coalesced
.section __TEXT,__const_coal,coalesced
.section __TEXT,__picsymbolstub4,symbol_stubs,none,16
.text   
.align 2
.globl _fill_neon_loop1
.private_extern _fill_neon_loop1
_fill_neon_loop1:
  push r4, r5, r6, r7, lr
  // r0 = wordPtr
  // r1 = inWord
  // r2 = numWordsToFill
  mov   r2, #1024
  // Load r1 (inWord) into NEON registers
  vdup.32 q0, r1
  vdup.32 q1, r1
  vdup.32 q2, r1
  vdup.32 q3, r1 (Stepping into this instruction jumps into __ARCLite__load)

NEONFILL16_loop1:
  vstm r0!, d0-d7
  sub r2, r2, #16
  cmp r2, #15
  bgt NEONFILL16_loop1

  mov   r0, #0
  pop r4, r5, r6, r7, pc
  .subsections_via_symbols

单步执行 ASM 指令,直到分配给 q3 的指令。当我跳过该指令时,代码似乎跳到了这里:

(gdb) bt
#0  0x0009a568 in __ARCLite__load () at /SourceCache/arclite_ios/arclite-31/source/arclite.m:529
#1  0x0007b050 in test_time_run_cases () at test_time.h:147

这真的很奇怪,我真的很不明白为什么分配给 NEON 寄存器会导致这种情况。 NEON 是否将 q3 用于我不知道的特殊事物?

我还尝试使用 dN(64 位 regs)加载寄存器,分配给 d7 的结果相同。

  vdup.32 d0, r1
  vdup.32 d1, r1
  vdup.32 d2, r1
  vdup.32 d3, r1
  vdup.32 d4, r1
  vdup.32 d5, r1
  vdup.32 d6, r1
  vdup.32 d7, r1

(稍后) 在弄乱了建议的更改之后,我找到了问题的根本原因。就是这个分支标签:

NEONFILL16_loop1:
  vstm r0!, d0-d7
  sub r2, r2, #16
  cmp r2, #15
  bgt NEONFILL16_loop1

由于某种原因,分支标签导致跳转到代码中的另一个位置。将上面的标签替换为以下内容即可解决问题:

1:
  vstm r0!, d0-d7
  sub r2, r2, #16
  cmp r2, #15
  bgt 1b

对于随 xcode 4.6 提供的 clang 中的 ASM 解析器版本,这可能是一些奇怪的事情,但无论如何只需更改标签即可修复它。

【问题讨论】:

您看到的可能只是一些编译器添加的代码吗? developer.apple.com/library/mac/#releasenotes/ObjectiveC/… 不,它与 Objective-C 无关,这是我试图运行的整个 .s,并且该模块仅包含 ARM NEON ASM 代码。它不应该依赖于 C 或 Objective-C 运行时。其他事情正在发生,但它必须是我刚刚错过的事情。这没有任何意义。 您是通过一些 C 代码调用它并使用 gdb 进行跟踪,对吗?我会反汇编代码,看看它是否包含对__ARCLite__load的调用。 【参考方案1】:

q3 既没有分配给某些特殊角色,也不需要保留。不用担心这个。

我认为 auselen 的猜测是正确的。看拆解就明白了。

试试下面这个:

.section __TEXT,__text,regular
.section __TEXT,__textcoal_nt,coalesced
.section __TEXT,__const_coal,coalesced
.section __TEXT,__picsymbolstub4,symbol_stubs,none,16
.text   
.align 2
.globl _fill_neon_loop1
.private_extern _fill_neon_loop1
_fill_neon_loop1:
  // r0 = wordPtr
  // r1 = inWord
  // r2 = numWordsToFill
  mov   r2, #1024
  // Load r1 (inWord) into NEON registers
  vdup.32 q0, r1
  vdup.32 q1, r1
  vdup.32 q2, r1
  vdup.32 q3, r1
  subs r2, r2, #16
  bxmi lr

NEONFILL16_loop1:
  vstm r0!, d0-d7
  subs r2, r2, #16
  bpl NEONFILL16_loop1

  mov   r0, #0
  bx lr
  .subsections_via_symbols

除了循环中的 cmp 之外,我还完全删除了保留的过时寄存器。 (你知道,我必须优化一切:))

如果 auselen 的猜测是正确的,这可能已经改变了跟踪时间,并且会在稍后的时间点进入 ARClite。

【讨论】:

我会用答案检查你,因为你的更改帮助我找到了问题的根本原因,如上所述。不过,我对您的更改有几个问题。为什么要添加一个潜艇,然后一个 bxmi?看起来如果 r2 寄存器包含值 16,那么这种方法将永远不会运行 vstm 指令,因为 bxmi 似乎从函数返回。另外,为什么使用 bpl 来分支而不是 bne 和 subs?您还提到 push/pop 不好,但它似乎在 ARM 文档中记录为最新,在这种情况下 bx lr 有什么好处? 通常,您将获取数据长度作为参数(在本例中为 r2)。如果将此值设为 0(内存访问冲突),您的原始实现将崩溃。如果是 16,则 bxmi 不会导致返回,因为结果为零,因此不是负数。而且由于我在循环之前做了一次减法,因此即使计数器达到零,也必须进行迭代。 (零属于正数) AtPCS 说 r4-r11 和 lr 必须在使用前保留,这意味着您只有在打算覆盖它们时才需要保留它们。在这种情况下,它们保持不变,并且不需要推送/弹出。 (它们需要花费周期)【参考方案2】:

几乎每次我在手写 ARM 代码中跳到一个奇怪的地方时,都是因为我摸索了拇指交互操作并且函数一直在错误的模式下执行——因此指令流对 CPU 来说就像垃圾一样,而且它随机跳跃,直到它自己受伤并摔倒。

对于所有作为函数入口点的标签,你应该有这个汇编指令:

.type _fill_neon_loop1, %function

这告诉链接器,当它修复BL 指令时,或者当它计算函数的地址时,它应该进行适当的调整以确保它以正确的模式执行。

【讨论】:

以上是关于iOS 上 NEON 代码的奇怪结果,跳转到 __ARCLite__load?的主要内容,如果未能解决你的问题,请参考以下文章

ionic 开发解决ios上qq客服链接不跳转或者跳转到appstore

SSE (Intel) 到 NEON (ARM) 数据类型类似物

微信公众号跳转到关注页面

NEON 中 _mm_hadd_ps 的等价物是啥?

dedecms_

Neon Intrinsics各函数介绍