3.5. Ahead of time 编译和 C++作绑定

为了实现最佳性能和无缝集成,Daslang 能够提前编译,即生成 C++ 文件,这些文件在语义上等同于模拟的 Daslang 节点。

输出 C++ 设计为在某种程度上是人类可读的。

在大多数情况下,Daslang 会自动生成 AOT,但自定义类型可能需要一些集成工作。 此外,可以通过额外的集成工作实现某些性能优化。

Daslang AOT 集成是在 AST 表达式树级别完成的,而不是在模拟节点级别完成的。

3.5.1. das_index

das_index 模板用于提供 ExprAtExprSafeAt 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 实现。