C 保留与具有内联函数的程序集的兼容性
我正在编写一些头文件,C代码和程序集都可以访问这些头文件。为此,使用C预处理器对汇编代码进行预处理 问题是这些头文件中有大量的C 保留与具有内联函数的程序集的兼容性,c,gcc,assembly,inline,gnu-toolchain,C,Gcc,Assembly,Inline,Gnu Toolchain,我正在编写一些头文件,C代码和程序集都可以访问这些头文件。为此,使用C预处理器对汇编代码进行预处理 问题是这些头文件中有大量的内联函数。汇编程序无法处理不是对象文件中的符号的函数(与静态内联函数一样),因此我无法使用这些函数。到目前为止,我已经阅读了一些非常有价值的帖子,并且已经掌握了如何将外部和静态与内联结合使用,但是我不确定如何使内联函数对C代码和汇编都可用 我目前的方法是在头文件中编写内联函数(使用>=GNU99,-O3内联函数,其他任何东西都调用该函数的外部定义,我需要明确定义),并在实
内联函数。汇编程序无法处理不是对象文件中的符号的函数(与静态内联
函数一样),因此我无法使用这些函数。到目前为止,我已经阅读了一些非常有价值的帖子,并且已经掌握了如何将外部
和静态
与内联
结合使用,但是我不确定如何使内联
函数对C代码和汇编都可用
我目前的方法是在头文件中编写内联
函数(使用>=GNU99,-O3
内联函数,其他任何东西都调用该函数的外部定义,我需要明确定义),并在实现文件中编写外部定义。C代码包括头文件(内联
函数)与-O3
编译,因此使用内联版本。汇编代码使用外部定义
问题:
汇编代码只能调用函数,目前无法进行内联。汇编代码可以通过任何方式使用内联吗?我的意思是在.S
文件中,不是内联程序集
extern inline
与我当前的方法类似,但它归结为一个定义(外部定义是自动发出的),因此它不能分为头文件和源文件,这对于C代码(头文件)和程序集(源文件)的可访问性至关重要
有没有更好的方法来实现我一直在努力的目标
一个调用
迫使您假设大多数寄存器被阻塞的开销相当高。要获得高性能,您需要手动将函数内联到asm中,以便能够完全优化所有功能
让编译器发出一个独立的定义并调用它应该只考虑对性能不重要的代码。你没有说你在asm中写了什么,或者为什么,但我认为这是性能关键。否则,您只需要用C编写它(我想,有什么特殊指令需要内联asm吗?)
如果您不想手动内联,并且希望在循环中使用这些小型内联C函数,那么用C编写整个过程可能会获得更好的性能。这将允许编译器跨更多代码进行优化
用于X8664的登记ARG调用约定是很好的,但是有很多寄存器都是被调用的,所以在计算的中间调用阻止了在寄存器中保持数据的生存。
汇编代码可以通过任何方式使用内联吗?我的意思是在一个
.S文件,而不是内联程序集
不,没有内联asm的相反语法。如果有,它会有这样的情况:告诉编译器输入的寄存器是什么,您想要输入的寄存器,以及允许它填充的寄存器
如果没有一个真正理解手工编写的asm的编译器,或者将其视为源代码,然后发出整个过程的优化版本,那么手工编写的asm和编译器输出之间的公共子表达式消除和其他重要优化是不可能的
Optimal将编译器输出内联到asm通常需要对asm进行调整,这就是为什么没有任何程序可以这样做的原因
有没有更好的方法来实现我一直在努力的目标
现在,您已经在评论中解释了您的目标:用C语言为您想要使用的特殊指令制作小包装,而不是相反
#include <stdint.h>
struct __attribute__((packed)) lgdt_arg {
uint16_t limit;
void * base; // FIXME: always 64bit in long mode, including the x32 ABI where pointers and uintptr_t are 32bit.
// In 16bit mode, base is 24bit (not 32), so I guess be careful with that too
// you could just make this a uint64_t, since x86 is little-endian.
// The trailing bytes don't matter since the instruction just uses a pointer to the struct.
};
inline void lgdt (const struct lgdt_arg *p) {
asm volatile ("lgdt %0" : : "m"(*p) : "memory");
}
// Or this kind of construct sometimes gets used to make doubly sure compile-time reordering doesn't happen:
inline void lgdt_v2 (struct lgdt_arg *p) {
asm volatile ("lgdt %0" : "+m"(*(volatile struct lgdt_arg *)p) :: "memory");
}
// that puts the asm statement into the dependency chain of things affecting the contents of the pointed-to struct, so the compiler is forced to order it correctly.
void set_gdt(unsigned size, char *table) {
struct lgdt_arg tmp = { size, table };
lgdt (&tmp);
}
我从未编写过涉及lgdt
的代码。像我一样使用“内存”缓冲区可能是一个好主意,以确保在编译时不会对任何加载/存储进行重新排序。这将确保它指向may的GDT在运行LGDT
之前完全初始化。(与LIDT
相同)。编译器可能会注意到base
为内联asm提供了对GDT的引用,并确保其内容同步,但我不确定。在这里仅仅使用“内存”重击器应该没有什么坏处
Linux(内核)在一条或两条指令周围使用这种包装器,在asm中编写尽可能少的代码。如果你想的话,去那里寻找灵感吧
回复:您的评论:是的,您需要在asm中编写引导扇区,可能还需要一些其他16位代码,因为gcc的-m16代码很愚蠢(基本上仍然是32位代码) 不,除了手动外,没有办法将C编译器输出内联到asm中。这是正常的,也是意料之中的,因为同样的原因,没有优化组装的程序。(即读取asm源、优化、写入不同的asm源)
想想这样一个程序必须做什么:它必须理解手写asm,才能知道它可以更改什么而不破坏手写asm。Asm作为一种源语言,没有给优化器提供太多的工作空间。您链接的答案解释了C99内联函数是如何工作的,但没有解释定义为何如此古怪。相关标准段落为ISO 9899:2011§6.7.4¨6-7(ISO 9899:1999同上): 六, 使用
内联
函数说明符声明的函数是内联函数。使函数成为内联函数意味着对函数的调用要尽可能快。138)这些建议的有效程度由实现定义。139)
七, 任何具有内部链接的函数都可以是内联函数。对于具有外部联动装置的功能
movw %di, -24(%rsp)
movq %rsi, -22(%rsp)
lgdt -24(%rsp)
ret
/* TODO: add assembly implementation */
inline int fast_add(int a, int b)
{
return (a + b);
}
inline int fast_mul(int a, int b)
{
return (a * b);
}
#include "fast_things.h"
extern inline int fast_add(int, int);
extern inline int fast_mul(int, int);
#ifndef EXTERN_INLINE
#define EXTERN_INLINE_UNDEFINED
#define EXTERN_INLINE inline
#endif
EXTERN_INLINE int fast_add(int a, int b)
{
return (a + b);
}
EXTERN_INLINE int fast_mul(int a, int b)
{
return (a * b);
}
#ifdef EXTERN_INLINE_UNDEFINED
#undef EXTERN_INLINE
#undef EXTERN_INLINE_UNDEFINED
#endif
#define EXTERN_INLINE extern inline
#include "fast_things.h"