9

这不是一个微不足道的问题。
注意:我不需要意见或建议使用纯 asm。我实际上需要完成我正在谈论的事情:在将结果分配给短整数时,在没有这个符号/零扩展 optcode 的情况下获得内联 asm。

我正在处理一个为许多功能滥用 16 位短裤的库,并且我正在对其进行优化。我需要使用内联 asm 添加一些优化的函数。问题是在许多地方函数的结果被分配给一个短整数。也就是说,编译器生成 uxth 或 sxth arm 操作码。

我的目标是避免这个问题,并确保不会生成这个无用的操作码。首先,我需要定义我的优化函数以返回 short int。这样,如果它被分配给一个 int 或一个短 int 就没有额外的操作码来转换结果。

问题是我不知道如何跳过编译器在我自己的函数中生成的 int->short 转换。
像这样的愚蠢演员:*(short*)(void*)&value不起作用。编译器要么开始弄乱堆栈制造问题,要么它仍然使用相同的 sxth 对结果进行符号扩展。

我为多个编译器编译,我能够为 arm 的 armcc 编译器解析它,但我无法用 GCC 完成它(我用 4.4.3 或 4.6.3 编译)。使用 armcc,我在内联 asm 语句中使用短类型。在 gcc 中,即使我使用短编译器仍然出于某种原因认为需要符号扩展。

这是一个我无法使用 GCC 的简单代码片段,关于如何让它工作的任何建议?对于这个简单的示例,我将使用 clz 指令:

示例文件test.c文件:

static __inline short CLZ(int n)
{
    short ret;
#ifdef __GNUC__
    __asm__("clz %0, %1" : "=r"(ret) : "r"(n));
#else
    __asm { clz ret, n; }
#endif
    return ret;
}

//test function
short test_clz(int n)
{
    return CLZ(n);
}



这是我使用 armcc -c -O3 得到的预期结果:

test_clz:
    CLZ      r0,r0
    BX       lr

这是 GCC -c -O3 给我的不可接受的结果:

test_clz:
    clz r0, r0
    sxth    r0, r0
    bx  lr

另请注意,如果用内部变量int ret;而不是short ret;armcc 重写 CLZ,则生成与 GCC 相同的结果。

使用 gcc 或 armcc 获取 asm 输出的快速行:
gcc -O3 -c test.c -o test.o && objdump -d test.o > test.s
armcc -O3 --arm --asm -c test.c

4

2 回答 2

6

编译器发生变化。特别是 gcc,你今天想出的技巧明天或昨天都行不通。并且不会在编译器(armcc、clang 等)中始终如一地工作。

1)删除短裤并用整数替换并结束它,这是一种选择,它是最不痛苦的解决方案。

2)如果你想要特定的asm,写特定的asm,不要乱来。也是一种选择。

虽然很可能编写出始终比其他代码编译得更好的代码,但您不能始终准确地获得所需的代码序列,而且不一致。从长远来看,您正在伤害自己,即使是编写自己的 asm 解决方案。您实际上正在寻找的解决方案是通过代码并用整数替换短裤,这将生成始终编译的代码比在那里有短裤更好。随着编译器的变化,它将花费更少的时间,并且不必每隔几个月就重写一次。

要彻底控制这一点,需要编译为 asm 或反汇编并删除有问题的指令,将函数留在 asm 中。快速轻松地完成任务,会给你想要的去除这个开销,只是留下一些不太可维护的东西。实际上,既然你有 armcc 做你想要在 armcc 中编译为 asm 的事情,那么就为 gnu 汇编程序习惯的愚蠢性对其进行修补,并将其用作一种解决方案(至少可以编写在 arm 工具和 gnu 下组装的 asm在手臂广告时代,在我失去对工具的访问权之前没有太多的 rvct 时间)。

有很多方法可以获取您提供的确切示例以给出您所追求的确切结果,但我严重怀疑这就是您所追求的,您会编写两行 asm 并完成。我的猜测是你试图在一个函数中内联一些东西(比 CLZ 大),同时仍然把它称为一个简短的,当调用它时,一个 int 会给你你想要的东西,而不需要内联 asm。(我仍然看不到内联 asm 与更改变量声明相比,无论是在哪里实现和测试都需要更少的时间,更少的输入,相同数量的代码来读取和测试)。

所以这是你的现实:

1) 穿着短裤及其副作用

2)将它们更改为整数

花几天、几周或几个月来做某事并不是什么大不了的事。大多数情况下,避免做某事需要几天、几周、几个月的时间。然后无论如何你都必须这样做,所以现在你有 2xdays、2xweeks、2xmonths ......无论什么解决方案,你都必须或应该测试它,你正在更改代码,所以这不是一个变化因素决定。使用内联汇编攻击编译器是您的最高风险,并且如果测试确实在时间等式中发生变化,则应该导致最多的测试。需要一些 gcc 版本,并且每 6 个月重新测试一次。

通常,asm 解决方案是在 abi 更改时,可能在重新测试之间间隔 10 年,而当我们从 64 位到 128 位时,修复 C 可能需要 20 年。但是 32 到 64 位的转换仍在进行中,我们还没有开始 ARM 32 到 64 位的转换/混合(不会放弃所有 64 位的 32 位 ARM 处理器,两者都将保留)。后端会一团糟,我现在不会和他们玩游戏。制作干净、可移植的 C 语言,您不依赖代码中 int 的大小(假设/要求最少 32,但确保它是 64 位干净的)是您最便宜的解决方案。

于 2012-06-03T06:34:59.260 回答
1

如果你追求的是速度,而不是代码大小,你可以试试这个:

static __inline short CLZ(int n)
{
    short ret;
#ifdef __GNUC__
    __asm__("clz %0, %1\n"
            "bx lr"
            : "=r"(ret) : "r"(n));
#else
    __asm { clz ret, n; }
#endif
    return ret;
}

更新添加:在我看来,gcc 编译器在这里做的是正确的事情。在C(相对于C++)中,没有返回 a 的函数之类的东西short——它总是自动转换为int. 所以你别无选择,只能愚弄编译器。如果您只是将文件名更改为 会发生什么test.cpp

于 2012-06-03T07:01:42.663 回答