Emscripten执行无效代码清除编译代码中未调用的函数。虽然这确实最小化了代码大小,但它可能删除了你打算本来要自己调用的函数(在编译代码之外)。
要确保在通用JavaScript中仍可以能够调用C函数,必须使用emcc命令行将其添加到EXPORTED_FUNCTIONS
中。例如,要防止函数my_func()
和main()
被删除或者重命名,请运行emcc
:
emcc -s“EXPORTED_FUNCTIONS = ['_main','_my_func']”......
注意
如果你有一个main()函数,_main应该在导出列表中,就像在那个例子中一样。否则,它将作为无效代码被删除;默认情况下,没有特殊的逻辑来保持main()有效。
注意
EXPORTED_FUNCTIONS
影响到JavaScript的编译。如果首先编译为对象文件,然后将对象编译为JavaScript,则需要在第二个命令上使用该选项。
如:
emcc -o ..... -s“EXPORTED_FUNCTIONS = ['_main','_my_func']”......
如果你的函数用于其他函数,LLVM可能会内联它,它不会在JavaScript中显示为唯一函数。通过使用EMSCRIPTEN_KEEPALIVE定义函数来防止内联:
void EMSCRIPTEN_KEEPALIVE yourCfunc(){..}
EMSCRIPTEN_KEEPALIVE
也导出该函数,就像它在EXPORTED_FUNCTIONS
上一样。
注意
- 所有没有使用
EXPORTED_FUNCTIONS
或EMSCRIPTEN_KEEPALIVE
保存的函数都可能被删除。为了确保使用这些方法,你需要使用其中的一种或两种来保持你想要的接口。 - 导出的函数需要是C函数(以避免被C++修改名称)。
- 如果你想一直跟踪显式导出的函数且这些导出不会被改变,则使用
EMSCRIPTEN_KEEPALIVE
修饰会很有用。它不一定适合从其他库导出函数 - 例如,修饰且重新编译C标准库的源代码不是一个好主意。如果以多种方式编译相同的源并更改导出的内容,那么使用命令行管理导出更容易。 - 运行emcc命令
-s LINKABLE = 1
将禁用链接时优化和消除无效代码。建议不要这样做,因为它会使代码变大和减少优化。
一个可能会引起丢失代码的可能是.a文件的链接不正确。 .a文件仅链接命令行上以前文件所需的内部对象文件,因此文件的顺序很重要,这可能会令人惊讶。如果要链接.a文件,请确保它们位于文件列表的末尾,并且它们之间的顺序正确。或者,只需在项目中使用.so文件。
提示
使用EMCC_DEBUG = 1为环境设置编译(在Linux上EMCC_DEBUG = 1 emcc ...
,在Windows上设置EMMCC_DEBUG = 1
)可能很有用。这将拆分编译步骤并将它们保存在/ tmp / emscripten_temp中。然后,您可以看到代码消失的阶段(您将需要在bitcode阶段上执行llvm-dis来读取它们,或者llvm-nm等)。