Page 137 - 《软件学报》2025年第8期
P. 137
3560 软件学报 2025 年第 36 卷第 8 期
(3) 调用结束后更新该变量的值.
对于时态运算符之间, 以及时态运算符和普通运算符之间的组合和嵌套的问题, 可以利用 Lustre 的特性进行
解决. 对于 Lustre 中的每个运算符, 在运算后将生成一个新的匿名流. 例如 x+1 将生成一个时钟周期与 x 相同, 而
值为 x+1 的新的匿名流. 因此, pre (x+1) 需要存储 x+1 的值; 而 pre pre x 实际上是对 pre x 这个流再做 pre 运算, 即
取 pre x 的上一个周期的值. 由此, 不需要考虑存储多个周期前的值的问题.
表 3 是时态算子目标码模式, 以 function 结尾的函数为辅助函数, 在编译器内以模板的形式存在, 当一个类型
的运算符被调用时, 就生成对应类型的辅助函数. ctx 是上下文 (context) 的缩写, 表示运算符依赖的之前周期的信
息, 如算法 1 所示. 其中 result_ctx_type 为程序上下文信息的集合, 其中包含了所有在源程序中需要用到的上下文
信息. pre_get_function 表示获取上一周期该<Expression> 的值, 借助变量 ctx (内存空间是运行时申请的). pre_
set_function 计算当前周期<Expression> 的值, 并将其赋给 ctx; arrow_function 首先判断当前是否处于初始周期, 若
是则返回<Expression1> 表达式的值, 否则, 返回<Expression2> 表达式的值.
表 3 时态算子目标码模式
产生式左部 产生式右部 目标码模式
temp = pre_get_function(ctx);
pre <Expression>
pre_set_function(<Expression>, ctx);
Expression <Expression1> → <Expression2> temp = arrow_function(<Expression1>, <Expression2>, ctx);
temp = arrow_function(<Expression1>, pre_get_function(ctx), ctx);
<Expression1> fby <Expression2>
pre_set_function(<Expression2>, ctx);
算法 1. ctx 结构体代码 (目标 C 代码).
typedef struct {
int _memory;
} pre_int_ctx_type;
typedef struct {
int test;
pre_int_ctx_type pre_int_ctx_tab[1];
} result_ctx_type;
算法 2 为最终生成的 pre 运算符的 ctx 结构体和辅助函数的目标代码. pre_int_ctx_type 是 pre 运算符针对 int
型的上下文信息, 因此其_memory 成员保存了 int 型变量. 在辅助函数 pre_int_get 和 pre_int_set 中, 分别对 ctx 中
的_memory 进行了存取. 若程序中有不止一个对 int 流取 pre 的操作, 则该辅助函数可以复用.
算法 2. 辅助函数代码实例 (目标 C 代码).
int pre_int_get(pre_int_ctx_type* ctx){
return ctx → _memory;
}
void pre_int_set(int s1, pre_int_ctx_type* ctx){
ctx → _memory = s1;
}
3.2.2 时钟算子
时钟算子 when、current 和 merge 是控制流的时钟的运算符. 其中 when 生成一个非基础周期的流, current 生
成一个基础周期的流, merge 将多个非基础周期的流合并为一个基础周期的流. 这里需要指出, Lustre 中的运算符
会生成一个新的匿名流, 而非改变输入的流. 例如 A when ck 将生成一个值取决于 A, 而时钟周期取决于 ck 的匿

