📜  编译器设计中的依赖图

📅  最后修改于: 2022-05-13 01:56:13.669000             🧑  作者: Mango

编译器设计中的依赖图

依赖图用于表示解析树中属性之间的信息流。在解析树中,依赖图基本上有助于确定属性的评估顺序。依赖图的主要目的是帮助编译器检查语句之间的各种类型的依赖关系,以防止它们以不正确的顺序执行,即以影响程序含义的方式执行。这是有助于识别程序的众多可并行化组件的主要方面。

它帮助我们确定更改的影响以及受其影响的对象。绘制边以连接依赖动作可用于创建依赖图。这些弧会导致操作之间的偏序,并且还会导致程序无法并行运行。尽管使用定义链接是一种依赖分析,但它会导致过度谨慎的数据依赖估计。在共享控制路径上,语句 I 和 j 之间可能存在四种依赖关系。

依赖图与其他有向网络一样,将节点或顶点描绘为带有名称的框或圆圈,以及在其强制遍历方向上将它们连接起来的箭头。依赖图通常在科学文献中用于描述语义链接、事件之间的时间和因果依赖关系以及电子电路中的电流流动。绘制依赖图在计算机科学中非常普遍,以至于我们需要使用一些工具来根据我们提供的一些基本文本指令自动执行该过程。

依赖类型:

依赖项大致分为以下几类:

1. 数据依赖:

当一个语句计算稍后由另一个语句使用的数据时。指令必须等待前一条指令的结果才能完成执行的状态。数据依赖性将触发处理器流水线的流动服务停止,或阻止使用流水线或超标量方法的高性能处理器中超标量处理器中指令的并行发布。

2.控制依赖:

控制依赖是来自程序有序控制流的依赖。如果前一条指令以允许其执行的方式评估,则程序指令执行的场景称为控制依赖。

3.流量依赖:

在计算机科学中,当程序语句引用前一个语句的数据时,就会发生流依赖。

4、反依赖:

当指令需要稍后修改的值时,这称为反依赖或读后写 (WAR)。指令 2 反依赖于以下示例中的指令 3;这些指令的顺序不能修改,也不能并行执行(可能会改变指令顺序),因为这会修改 A 的最终值。

5. 输出依赖:

当指令的执行顺序对变量的最终输出值产生影响时,就会发生输出依赖性,也称为写后写 (WAW)。在下面的示例中,指令 3 和 1 之间存在输出依赖关系;改变指令的顺序会影响 A 的最终值,因此这些指令不能并行运行。

6. 控制依赖:

如果 A 的结果决定了是否应该执行 B,则指令 B 对先前的指令 A 具有控制依赖关系。在以下示例中,显示方式 S 2S 2 指令对显示方式 S 1S 1 指令具有控制依赖关系.但是,显示风格 S 3S 3 并不依赖于显示风格 S 1S 1,因为无论显示风格 S 1S 1 的结果如何,显示风格 S 3S 3 总是会执行。

依赖图示例:

为以下语法设计依赖图:

E -> E1 + E2
E -> E1 * E2 
                     PRODUCTIONS                                       SEMANTIC RULES                    

E -> E1 + E2

E -> E1 * E2 

E.val -> E1.val + E2.val

E.val -> E1.val * E2.val

上述语法所需的依赖图表示为 -

上述示例的依赖关系图

  1. 合成的属性由.val表示。
  2. 因此, E.valE1.valE2.val具有合成属性。
  3. 依赖关系用黑色箭头表示。
  4. E1 和 E2 的箭头表示 E 的值取决于 E1 和 E2。

依赖解析:

依赖关系解析是一个两阶段的过程,一直执行到依赖关系图完成。

  1. 当向图中引入新的依赖关系时执行冲突解决以决定应该添加哪个版本。
  2. 在特定的依赖关系中,例如,一个版本化的模块,它被标识为图形的一部分,它有助于提取其信息,从而有助于一一添加其依赖关系。

在进行依赖解析时,Gradle(自动化工具)会处理两种类型的冲突:

版本冲突:

版本冲突是当两个组件依赖于同一个模块但版本不同时发生的冲突。

例如:

假设该项目依赖于 Facebook 的 react 库,即“com.google.react:react:18.7.0”。这里的版本是 18.7.0。现在我们可以清楚地看到它也依赖于其他一些库,这些库本身依赖于 react 但版本是 19.0.2 完全不同。

Gradle 通过选择最高版本来解决这个问题。在这种情况下,将选择 19.0.2。

但这不是商店的尽头。 Gradle 具有丰富版本声明的概念,并且有多种方法可以从各种选项中选择版本。

实现冲突:

实现冲突是指依赖关系图包含多个提供相同实现或 Gradle 术语中的功能的模块的情况。 Gradle 使用变体和功能确定模块提供的内容。这是一个独一无二的功能,需要在自己的章节中充分理解它的含义和允许的内容。当两个模块发生冲突时:

  1. 尝试选择不兼容的变体,
  2. 声明相同的能力

依赖图的用途:

  1. 依赖图背后的主要思想是让编译器检查语句之间的各种类型的依赖关系,以防止它们以不正确的顺序执行,即以影响程序含义的方式执行。
  2. 这有助于它识别程序的众多可并行化组件。
  3. 自动化软件安装程序:他们在图表中寻找需要但尚未安装的软件包。包的耦合决定了依赖。
  4. 指令调度以更广泛的方式使用依赖关系。
  5. 依赖图广泛用于死代码消除。