翻译:云荒杯倾
本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。
也能够去做者的博客阅读文章。
欢迎加入Wasm和emscripten技术交流群,群聊号码:939206522。html
Emscripten代码移植主题涵盖了将C、C++代码移植到Emscripten时须要考虑的全部核心考虑问题,以及通常的编码和调试指南。
共有如下主题。c++
每一部份内容都比较多,本文主要讲第一部分,代码可移植性与限制。下面是正文:git
Emscripten几乎能够编译任何可移植的c/c++代码到JavaScript。但因为浏览器环境限制和Emscripten编译出来的代码的限制,一些代码为了能被编译须要作改动,本文就帮咱们找出这部分代码。程序员
本节解释了哪些类型的代码是不可移植的(或者更难于移植);哪些代码能够编译,但会运行得很慢。开发人员可使用这些信息来评估移植代码和重写代码的工做量。github
为了使Emscripten工做,下面类型的代码须要重写。(理论上,在使用模拟的状况下,可使用Emscripten解决这些问题,但速度很是慢。)web
Note: 若是JavaScript标准机构将共享状态添加到webworker中,支持多线程代码将成为可能。
SAFE_HEAP = 1构建您的代码,那么您将获得一个清晰的运行时异常,参见调试。编程
jumping down the stack, but not jumping up to an unwound stack, which is undefined behavior).segmentfault
NOTE: 若是你是一个喜欢本身写垃圾回收程序的程序员,可能对这类代码比较熟。。。
Note: 当你要优化代码的时候,就会知道了解这些事项是有用的。
下面类型的代码会被编译,可是可能运行的很慢:windows
浏览器环境和JavaScript不一样于C/C++一般运行的本地环境。这些差别对如何调用和使用本地API施加了一些限制。本部分列出了一些比较明显的限制。api
Emscripten支持libc库的网络函数,但您必须限制他们是异步(非阻塞)操做。这是由于底层的JavaScript网络函数是异步的。
Emscripten支持libc文件系统函数,C /C++代码能够以正常方式编写。
在浏览器环境中运行的代码是沙盒sandboxed,而且不直接访问本地文件系统。而后,Emscripten就建立了一个虚拟文件系统,它能够预装数据,或者连接到url来懒加载。这会影响同步文件系统函数调用以及一个项目如何被编译。关于这方面,请参见文件系统概述。
浏览器事件模型使用合做模式的多任务处理——每一个事件都有一个运行的“turn”,而后必须将控制权返回给浏览器事件循环,这样其余事件就能够处理了。
HTML页面挂起的一个常见缘由是JavaScript未完成而且未将控制权返回给浏览器。
这将影响含有死循环的主函数的代码编写。有关更多信息,请参见Emscripten Runtime环境。
函数指针有三个主要的问题:
一、指针类型转换会引发指针调用失败。
针对函数声明时的签名不一样,函数指针会被存储到不一样的表中。当一个函数被调用时,它会在与当前函数指针签名关联的表中搜索它。若是你进行了指针类型转换,而指针和全部的表并无被修改,则调用代码将在错误的表中查找。而错误的表中实际上极可能并无一个叫该名字的指针,这样就出错了。
例如,一个声明为int(int)(返回int,接收int)的函数,会被添加到表FUNCTION_TABLE_ii。若是您将一个指向该函数指针投射到void(int)(不返回,接收int),那么代码将在FUNCTION_TABLE_vi中查找函数。
你可能看到编译警告:
warning: implicit declaration of function
推荐的解决方案是重构代码以免这种状况,以下面的Asm指针转换所描述的那样。
二、当你使用-o2以及更高优化级别的时候,比较不一样类型的函数指针会产生错误的结果,而错误的函数指针可能更具误导性。要检查你的代码出问题的缘由,能够将aliasing_function_pointer设为零,(- s aliasing_function_pointer= 0)进行编译。
NOTE: 在asm.js中,函数指针存储在特定函数类型的表中。如FUNCTION_TABLE_ii。 在较低级别的优化中,每一个函数指针在全部函数类型表上都有一个唯一的索引值(一个函数指针只在其中一个表的某个索引位置存在,在全部其余表中 这个索引位置都是一个空槽)。所以,比较函数指针(索引)能给出了一个准确的结果,但若是是试图在错误的表中调用函数指针,将会抛出一个错误,由于该索引是空的。 在-o2和更高级别的优化设置下,表被优化,以致于全部函数指针都在顺序索引中。这是一个有用的优化,由于若是没有全部空槽,表就更紧凑, 但它确实意味着函数索引再也不是“全局”的唯一(由于一个函数指针在这张表中的索引位置与在另外一张表中的索引位置不一样了)。此时须要一张特定的表 和在这样表中的特定位置索引才可以惟一索引到一个函数。 所以,高级别的优化编译: 一、因为不一样类型的函数能够有相同的索引(尽管在不一样的表中),函数指针的比较可能会产生错误的结果。 二、函数指针代码中的错误更难于调试,由于它们致使错误的代码被调用,而不是显式的错误(就像在表中的“漏洞”中那样)。
三、结构体按值传递时,老版本的clang会为c和c++代码生成两种不一样的代码,这两种格式的代码不兼容,你可能会收到一个警告。
解决方案是按引用传递结构体,或者不要在有结构体的位置混淆c和c++(好比,重命名.c为.cpp)。
如上所述,在asm.js模式下,函数指针必须使用正确的类型调用,不然调用将失败。这是由于在函数声明的时候,每一个函数指针会基于这个函数的签名被存储在一个特定的表中: 将指针转换为另外一个类型会致使调用代码在错误的位置(表)查找函数指针。
NOTE: 对于每种类型的函数指针都有一个单独的表,可让JavaScript引擎知道每一个函数指针调用的确切类型,这样也好进而优化他们。
有三种解决办法,优先选择第二种:
添加额外的参数/删除参数/更改参数类型/添加或删除返回类型等。这能够增长显著的运行时开销,所以不推荐,但值得尝试。
本页面列出了一些 与Emscripten编译出来的应用程序和游戏相关的 主要浏览器的最新版本之间的差别:
Opera 12.16和Windows谷歌Chrome 28.0.1500.95有一个限制,即计时器的精度仅为毫秒。
在其余主流浏览器上(IE10,firefox22,非windows的Chrome 28),也都是亚毫秒精度。
这意味着不可能使用浏览器的图像编解码器来解码Emscripten虚拟文件系统中的预加载文件。
Emscripten代码移植主题系列文章是emscripten中文站点的一部份内容。
第一个主题介绍代码可移植性与限制
第二个主题介绍Emscripten的运行时环境
第三个主题第一篇文章介绍链接C++和JavaScript
第三个主题第二篇文章介绍embind
第四个主题介绍文件和文件系统
第六个主题介绍Emscripten如何调试代码