- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我与一位专家合作,据称他的编码技能比我自己高得多,他对内联汇编的理解比我以往任何时候都好。
其中一项主张是,只要操作数作为输入约束出现,您就无需将其列为破坏项或指定寄存器可能已被内联程序集修改。当其他人试图通过 memset
寻求帮助时,对话就开始了。以这种方式有效编码的实现:
void *memset(void *dest, int value, size_t count)
{
asm volatile ("cld; rep stosb" :: "D"(dest), "c"(count), "a"(value));
return dest;
}
"c"(count) already tells the compiler c is clobbered
void swap_vbufs(void) {
asm volatile (
"1: "
"lodsd;"
"cmp eax, dword ptr ds:[rbx];"
"jne 2f;"
"add rdi, 4;"
"jmp 3f;"
"2: "
"stosd;"
"3: "
"add rbx, 4;"
"dec rcx;"
"jnz 1b;"
:
: "S" (antibuffer0),
"D" (framebuffer),
"b" (antibuffer1),
"c" ((vbe_pitch / sizeof(uint32_t)) * vbe_height)
: "rax"
);
return;
}
antibuffer0
,
antibuffer1
, 和
framebuffer
内存中的所有缓冲区都被视为
uint32_t
的数组.
framebuffer
是实际显存 (MMIO) 和
antibuffer0
,
antibuffer1
是在内存中分配的缓冲区。
volatile uint32_t *framebuffer;
volatile uint32_t *antibuffer0;
volatile uint32_t *antibuffer1;
int vbe_width = 1024;
int vbe_height = 768;
int vbe_pitch;
"add rbx, 4;"
"dec rcx;"
+
修饰符)。即使有人试图争辩说 64 位 System V ABI 调用约定将保存它们(假设编写此类代码的方式很糟糕,恕我直言)RBX 是一个非 volatile 寄存器,它将在此代码中更改。 swap_vbufs
未经版权所有者许可,函数和相关变量声明已逐字复制 fair use用于对更大范围的工作进行评论。 最佳答案
从各方面来说你都是正确的,这段代码对编译器来说充满了谎言,可能会咬你。 例如使用不同的周围代码,或不同的编译器版本/选项(尤其是链接时优化以启用跨文件内联)。swap_vbufs
甚至看起来效率不高,我怀疑 gcc 与纯 C 版本相同或更好。 https://gcc.gnu.org/wiki/DontUseInlineAsm 。 stosd
在英特尔上是 3 uops,比普通的 mov
-store + add rdi,4
差。并且使 add rdi,4
无条件将避免对 else
块的需要,该块将额外的 jmp
放在(希望)快速路径上,因为缓冲区相等,因此没有 MMIO 存储到视频 RAM。
( lodsd
在 Haswell 和更新版本上只有 2 个 uops,所以如果您不关心 IvyBridge 或更旧版本也没关系)。
在内核代码中,我猜他们正在避免 SSE2,即使它是 x86-64 的基线,否则您可能想要使用它。对于普通的内存目标,您只需将 memcpy
与 rep movsd
或 ERMSB rep movsb
一起使用,但我想这里的重点是通过检查视频 RAM 的缓存副本来尽可能避免 MMIO 存储。尽管如此,使用 movnti
的无条件流存储可能是有效的,除非视频 RAM 映射到 UC(不可缓存)而不是 WC。
很容易构建在实践中确实会中断的示例,例如在同一函数中的内联 asm 语句之后再次使用相关的 C 变量。 (或在内联 asm 的父函数中)。
您要销毁的输入通常必须使用匹配的虚拟输出或带有 C tmp var 的 RMW 输出来处理,而不仅仅是 "r"
。或 "a"
。"r"
或任何特定寄存器约束,如 "D"
意味着这是一个只读输入,编译器可以期望在之后找到不受干扰的值。没有“我想销毁的输入”约束;您必须将其与虚拟输出或变量进行合成。
这一切都适用于支持 GNU C 内联 asm 语法的其他编译器(clang 和 ICC)。
从 GCC 手册: Extended asm
Input Operands :
Do not modify the contents of input-only operands (except for inputs tied to outputs). The compiler assumes that on exit from the asm statement these operands contain the same values as they had before executing the statement. It is not possible to use clobbers to inform the compiler that the values in these inputs are changing.
rax
clobber 会导致使用
"a"
作为输入出错;clobbers 和操作数不能重叠。)
int plain_C(int in) { return (in+1) + in; }
// buggy: modifies an input read-only operand
int bad_asm(int in) {
int out;
asm ("inc %%edi;\n\t mov %%edi, %0" : "=a"(out) : [in]"D"(in) );
return out + in;
}
addl
将 edi
用于 in
,即使内联 asm 使用该寄存器作为输入 。 (因此中断,因为这个有问题的内联 asm 修改了寄存器)。在这种情况下,它恰好持有
in+1
。我使用了 gcc9.1,但这不是新行为。
## gcc9.1 -O3 -fverbose-asm
bad(int):
inc %edi;
mov %edi, %eax # out (comment mentions out because I used %0)
addl %edi, %eax # in, tmp86
ret
auto tmp = in; asm("..." : "+r"(tmp));
)
int safe(int in) {
int out;
int dummy;
asm ("inc %%edi;\n\t mov %%edi, %%eax"
: "=a"(out),
"=&D"(dummy)
: [in]"1"(in) // matching constraint, or "D" works.
);
return out + in;
}
# gcc9.1 again.
safe_asm(int):
movl %edi, %edx # tmp89, in compiler-generated save of in
# start inline asm
inc %edi;
mov %edi, %eax
# end inline asm
addl %edx, %eax # in, tmp88
ret
"lea 1(%%rdi), %0"
会通过不修改输入来避免这些问题,
mov
/
inc
也是如此。这是一个故意破坏输入的人为示例。
a potential bug that the compiler hasn't been told that memory that these pointers are pointing at has been read and or modified.
arr[2] = 1;
asm(...);
arr[2] = 0;
arr[2]
不是 asm 的输入,而只是
arr
地址本身,它将执行死存储消除并删除
=1
分配。 (或者将其视为使用 asm 语句重新排序商店,然后将 2 个商店折叠到同一位置)。
"m"(*arr)
也不适用于指针,只能用于实际数组。该输入操作数只会告诉编译器
arr[0]
是一个输入,仍然不是
arr[2]
。如果这就是你所有的 asm 读取,那是一件好事,因为它不会阻止其他部分的优化。
memset
示例,要正确声明指向的内存是输出操作数,请将指针转换为指向数组的指针并取消引用它,以告诉 gcc 整个内存范围是操作数。
*(char (*)[count])pointer
。 (您可以将
[]
留空以指定通过此指针访问的任意长度的内存区域。)
// correct version written by @MichaelPetch.
void *memset(void *dest, int value, size_t count)
{
void *tmp = dest;
asm ("rep stosb # mem output is %2"
: "+D"(tmp), "+c"(count), // tell the compiler we modify the regs
"=m"(*(char (*)[count])tmp) // dummy memory output
: "a"(value) // EAX actually is read-only
: // no clobbers
);
return dest;
}
(%rdi)
,因此它愿意使用同时也是输入/输出操作数的寄存器。
void
函数(或内联到不使用返回值的函数之后),在让
rep stosb
销毁它之前不必将指针 arg 复制到任何地方。
关于GCC 代码似乎违反了内联汇编规则,但专家认为并非如此,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56142479/
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
class Person def name puts "Doharey" end end puts Person.class #=> this out puts Class puts
在 PHP 中,($myvariable==0)当 $myvariable 为零时,表达式的值为真;当 $myvariable 为 null 时,此表达式的值也为 true。如何排除第二种情况?我的意
正文 Oracle的一顿猛如虎操作,让开发者彻底失去了Java EE。Eclipse基金会则自立门户,另起炉灶开启Jakarta EE项目。 对于Jakarta EE,从它
我是 python 新手,建议我使用 Canopy。我正在努力跟进 with this tutorial ,但我陷入了 mahotas.imread 行。我收到一个错误,说以这个结尾: Full er
上下文是我们想要跟踪应用程序的用户行为,因为它具有不同的功能。 为此,我们创建了一个自定义 Angular Directive(指令),例如myFunctionality并将 HTML 部分包装到此指
我正在尝试在文本字段中实现 google Places api 的自动完成功能。这是我的代码: $(document).ready(function() { initialize(){ v
我在 Glassfish 3.1.1 中配置了一个新的 jdbcRealm 并打开了 FINEST 日志记录,当我尝试使用用户名和密码登录时,我得到以下信息。它提示我的 Web 应用程序映射到的领域是
问题是,即使我将线程设置为“thrd.IsBackground = false”,iis 也不认为它正在运行,即使这是一个长时间运行的进程。如果我不关闭应用程序池的空闲关闭,它将关闭,因为它认为它是空
我正在使用 OpenJDK 8(从 https://jdk.java.net/java-se-ri/8 下载并解压,添加到 PATH),并且遇到了证书错误。 经过调查,我意识到 cacerts 存在问
我基于 Firebase 制作了简单的后期制作项目。我将帖子保存到 Firebase 中,如下所示: let data = UIImageJPEGRepresentation(newPostImage
我觉得还是先说明情况比较好。 情况 我正在编写一些软件来过滤 Set 的 File。 过滤器如下:如果文件未隐藏,则将其添加到新的 Set。 问题在于 File.isHidden() 的当前行为如下:
我创建了一个 C++ DLL 函数,它使用多个数组来处理最终的图像数据。我正在尝试通过引用传递这些数组,进行计算,然后通过预分配数组中的引用将输出传回。在该函数中,我使用了 Intel Perform
我在 python 中有一个小应用程序,除了这个小问题之外,它工作得很好:它应该连续运行一个循环,直到用户通过按钮告诉它停止,但是当我点击开始按钮时,Windows 告诉我它不是回应。现在,如果我编写
代码运行正常,但我怎么会得到这个错误日志 错误日志: 08-28 08:44:24.281: E/MediaPlayer(32454): mOnVideoSizeChangedListener is
我有一个使用 Karma+Jasmine 和 JSHint 的 Grunt 设置。每当我在我的规范文件上运行 JSHint 时,我都会收到一系列“未定义”错误,其中大部分是针对 Jasmine 的内置
将以下代码保存到文件中,Ubuntu 14.04 正确地意识到它是 bash: #!/usr/bin/env bash awk '{print $1 $2}' my_file 然而,向 awk 添加关
以下代码返回 false import inspect print(inspect.isbuiltin(map)) 但是 map 功能在"built-in" functions下列出. 为什么会这样?
这是一段常见的示例代码: while (1) { print "foo\n"; } 永远打印“foo”。 perl foo.pl foo foo foo ... 和 while (0) { p
我对 Haskell 比较陌生,来自 F#(一种 Microsoft 语言)。 我已经从脚手架创建了一个 Yesod 项目,稍微玩了一下,调整了一些东西,但随后它停止工作,并显示此错误消息(在所有模块
我是一名优秀的程序员,十分优秀!