Sim*_*low 4 c++ assembly linker dynamic
要求:对于某个项目,我们有独特的要求。该应用程序支持一种表达式语言,允许用户定义自己的复杂表达式,这些表达式可以在运行时(每秒数百次)进行计算,并且需要在机器级别执行以提高性能。
工作:我们的表达式解析器完美地将脚本翻译成相应的汇编语言程序。我们通过静态链接用我们的 C 测试程序生成的目标文件来检查它,它们产生正确的结果。由于客户端可以随时更改脚本,我们的程序(在运行时)会检测到更改,调用生成相应汇编例程的解析器。然后我们从后端调用汇编器来创建目标代码。
问题
我们如何从 C++ 程序(Loader)动态调用这个汇编程序?
我们不应该调用 C++ 编译器将它与加载器链接,因为加载器已经有其他子例程在运行,我们不能关闭加载器,重新编译然后执行新的加载器程序。
我尝试在线搜索解决方案,但每次结果都充斥着 .NET 程序集动态调用。我们的应用程序与 .NET 无关。
首先,“生成的插件”方法(在 Linux 上;我的答案侧重于 Linux,但可以通过一些努力适应 Windows;您可以使用多平台框架,如Qt或POCO或GTK 的Glib ;然后所有包装插件加载能力à洛杉矶dlopen有一个通用的API,你可以在Windows上使用,在Linux,MacOSX上,在Android):
/tmp/generated01.c(您甚至可以使用标准 C++ 容器生成 C++ 代码,但其编译速度会明显变慢;注意名称修改,因此发出和使用extern "C"函数;阅读C++ dlopen mini HowTo)。请参阅此答案解释为什么生成 C 是值得的(并且可能比生成汇编代码更好,更便携)。fork+ execve+ waitpid,或简单地system)生成文件的汇编成一个共享对象/tmp/genenerated01.so通过运行gcc -fPIC -Wall -O /tmp/generated01.c -shared -o /tmp/generated01.so命令; 您实际上需要获得与位置无关的代码,因此是-fPIC标志。如果dlopen在生成的汇编代码上使用,则需要改进汇编生成器以发出 PIC 代码。dlopen那个新的/tmp/generated01.so(所以使用动态链接器),见dlopen(3);你甚至可以remove生成现在无用的 C 文件/tmp/generated01.cdlsym获取指向生成代码的函数指针的相关符号,请参阅dlsym(3);您的应用程序将使用这些函数指针简单地调用生成的代码。dlclose该共享对象库(但您可能会接受通过根本不调用来泄漏一些地址空间dlclose)上述方法是值得的,可以多次使用(我的manydl.c演示了你可以dlopen有一百万个不同的共享对象),并且实际上甚至兼容(即使在发出 C 代码时!)与交互式 读取评估- Print-Loop - 在大多数当前的台式机、笔记本电脑和服务器上 - 因为在大多数情况下生成的内容/tmp/generated01.c非常小(例如最多几百行),可以非常快速地生成和编译(通过 gcc等...)。我什至在MELT中将其用于 REPL 模式。在 Linux 上,这种插件方法通常需要链接主应用程序-rdynamic(以便dlopen-ed 插件可以从主应用程序引用和调用函数)。
然后,其他方法可能是使用一些Just-In-Time 编译库,例如
大体上讲,该列表的第一个元素能够相当快地发出 JIT 机器代码,但该代码的运行速度不如编译gcc -fPIC -O1或-O2等效生成的 C 代码(但运行速度通常会慢 2 到 5 倍!);最后两个元素(LLVM 和 GCCJIT)是基于编译器的:因此它们能够优化和发布高效的代码,但代价是较慢的 JIT 代码发布。所有 JIT 库都能够(就像dlsym插件一样)为新的 JIT 构造函数提供函数指针。
请注意,需要进行权衡:某些技术能够快速生成一些机器代码,如果您接受该生成的代码稍后运行有点慢;其他技术(特别是 GCCJIT 或 LLVM)花费时间来优化生成的机器代码,因此需要更多时间来发出机器代码,但该代码稍后会快速运行。您不应该同时期待(小代时间,快速执行时间),因为天下没有免费的午餐。
我相信手动生成一些汇编代码实际上是不值得的。您将无法生成非常优化的代码(因为优化是一门非常困难的艺术,而且 GCC 和 Clang 都有数百万条用于优化传递的源代码行),除非您为此花费多年的时间。使用一些 JIT 库更容易,并且“编译”为 C 或 C++ 也很容易(您将优化的负担留给您正在调用的 C 编译器)。
您还可以考虑将您的应用程序重写为具有同象性和元编程能力(例如多阶段编程)的某种语言,例如Common Lisp(以及许多其他语言,例如提供eval 的语言)。它的SBCL实现总是发出机器代码......
你也可以在你的应用程序中嵌入一个解释器,比如Lua -甚至LuaJit - 或Guile。嵌入现有语言的主要优点是有资源(书籍、模块等)和了解它们的社区(设计一种好的语言很困难!)。此外,嵌入式解释器库设计良好,可能调试良好(因为使用了很多),其中一些已经足够快(因为使用字节码技术)。