3.5. Ahead of time 编译和 C++作绑定
为了实现最佳性能和无缝集成,Daslang 能够提前编译,即生成 C++ 文件,这些文件在语义上等同于模拟的 Daslang 节点。
输出 C++ 设计为在某种程度上是人类可读的。
在大多数情况下,Daslang 会自动生成 AOT,但自定义类型可能需要一些集成工作。 此外,可以通过额外的集成工作实现某些性能优化。
Daslang AOT 集成是在 AST 表达式树级别完成的,而不是在模拟节点级别完成的。
3.5.1. das_index
das_index
模板用于提供 ExprAt
和 ExprSafeAt
AST 节点的实现。
给定输入类型 VecT,输出结果 TT,索引类型为 int32_t,das_index
需要实现以下函数:
// regular index
static __forceinline TT & at ( VecT & value, int32_t index, Context * __context__ );
static __forceinline const TT & at ( const VecT & value, int32_t index, Context * __context__ );
// safe index
static __forceinline TT * safe_at ( VecT * value, int32_t index, Context * );
static __forceinline const TT * safe_at ( const VecT * value, int32_t index, Context * );
请注意,有时可能有多个索引类型。 在这种情况下,需要为每个索引类型实现。
请注意 const 和 not const 版本如何可用。
此外,可能需要 das_index
模板本身的 const 和非 const 版本。
3.5.2. das_iterator
das_iterator
模板用于为 ExprFor
源提供 for 循环后端。
让我们回顾一下以下示例,该示例在 range 类型上实现迭代:
template <>
struct das_iterator <const range> {
__forceinline das_iterator(const range & r) : that(r) {}
__forceinline bool first ( Context *, int32_t & i ) { i = that.from; return i!=that.to; }
__forceinline bool next ( Context *, int32_t & i ) { i++; return i!=that.to; }
__forceinline void close ( Context *, int32_t & ) {}
range that;
};
das_iterator``模板需要实现指定类型的构造函数,以及 ``first
, next
, 和 ``close``函数,类似于 Iterator 的函数。
将提供 das_iterator
模板的 const 和 regular 版本:
template <>
struct das_iterator <range> : das_iterator<const range> {
__forceinline das_iterator(const range & r) : das_iterator<const range>(r) {}
};
Ref 迭代器返回类型是 C++ 指针:
template <typename TT>
struct das_iterator<TArray<TT>> {
__forceinline bool first(Context * __context__, TT * & i) {
开箱即用,das_iterator
为所有集成类型实现。
3.5.3. AOT 模板功能
默认情况下,AOT 生成的函数希望将块作为 C++ TBlock 类传递(参阅 Blocks). 这会产生显著的性能开销,而 AOT 模板机制可以减少这种开销。
让我们回顾一下以下示例:
void peek_das_string(const string & str, const TBlock<void,TTemporary<const char *>> & block, Context * context) {
vec4f args[1];
args[0] = cast<const char *>::from(str.c_str());
context->invoke(block, args, nullptr);
}
开销包括类型封送处理以及上下文块调用。 但是,以下模板可以像这样调用:
template <typename TT>
void peek_das_string_T(const string & str, TT && block, Context *) {
block((char *)str.c_str());
}
在这里,块是模板化的,可以在没有任何编组的情况下调用。 为此,需要修改模块中的函数注册:
addExtern<DAS_BIND_FUN(peek_das_string)>(*this, lib, "peek",
SideEffects::modifyExternal,"peek_das_string_T")->setAotTemplate();
3.5.4. 单个功能的 AOT 设置
有几个函数注释可以控制函数 AOT 的生成方式。
[hybrid]
注释表示函数始终通过完整的 Daslang 互作 ABI(较慢)调用,而不是通过 C++ 语言构造直接调用函数(较快)。
这样做可以消除语义哈希中两个函数之间的依赖关系,从而只允许将其中一个函数替换为模拟版本。
[no_aot]
注解表示不会生成函数的 AOT 版本。
这对于解决 AOT 代码生成问题以及在内置模块开发期间非常有用。
3.5.5. AOT 前缀和后缀
函数或类型特征表达式可以具有自定义注释,以指定生成的调用周围的前缀和后缀文本。 这对于完全替换调用本身、提供其他类型转换或执行其他自定义可能是必要的。
让我们回顾一下以下示例:
struct ClassInfoMacro : TypeInfoMacro {
....
virtual void aotPrefix ( TextWriter & ss, const ExpressionPtr & ) override {
ss << "(void *)(&";
}
virtual void aotSuffix ( TextWriter & ss, const ExpressionPtr & ) override {
ss << ")";
}
在这里,类 info 宏将请求的类型信息转换为 void *。
类机制的这一部分允许类的 __rtti
指针保持为空,而不在包含类的所有位置都包含 RTTI。
3.5.6. AOT 字段前缀和后缀
ExprField
被 handled 类型注释中的以下函数覆盖(参阅 Handles):
virtual void aotPreVisitGetField ( TextWriter &, const string & fieldName )
virtual void aotPreVisitGetFieldPtr ( TextWriter &, const string & fieldName )
virtual void aotVisitGetField ( TextWriter & ss, const string & fieldName )
virtual void aotVisitGetFieldPtr ( TextWriter & ss, const string & fieldName )
默认情况下,前缀函数不执行任何作,后缀函数会相应地附加 .fieldName 和 ->fieldName 。
请注意,ExprSafeField
还没有被覆盖,它将在某个时候为 AOT 实现。