【问题标题】:What is the smallest subset of C that modern compilers can optimise as well as C? [closed]现代编译器可以像 C 一样优化 C 的最小子集是什么? [关闭]
【发布时间】:2020-09-20 19:34:54
【问题描述】:

我想生成一些 C 并验证它是否可以编译,因此我想使用 C 的一个子集来简化验证。 但是我不想限制可以编写的内容,所以我想要 C 的一个子集,这样给定任何 C 程序,您都可以编写一个程序,该程序将在现代编译器中编译成相同的机器代码。

例如,我认为以下构造是不必要的:

  • x++, ++x, x—-, —-x
  • (编辑:这些不是,感谢@dmuir 和@StoryTeller-UnslanderMonica)+=-=*=/=%=<<=,@9876543325@,@98764 @、^=|=
  • a[i]
  • 声明之外的数组类型
  • p->m

对于某些优化,是否还有其他需要我的?

【问题讨论】:

  • 我不太确定 += 等。假设 e 是一个涉及调用具有副作用的函数的复杂表达式;那么 e += 1 将与 e = e + 1 不同,因为在第二种形式中副作用会发生两次。
  • 您认为只将简单对象视为操作数。对于用于生成左值的函数调用,例如*f() += 1。结果将不一样,因为*f() = *f() + 1 必须调用函数两次,而简写只调用一次。这会影响行为。所以这是需要注意的事情。
  • 这没有多大意义。某个功能可能是多余的,因为在功能方面存在类似的功能,但这并不一定意味着它们在性能方面是等效的。有很多微妙的废话,例如 x += y 在评估方面与 x = x + y 不完全相同,或者 ?: 由于隐式提升,与 if-else 不完全相同,某些布尔检查在功能上是等效的,但是其中一个生成分支等。
  • 这个问题是荒谬的。既定目标“我想生成一些 C 并验证它是否可以编译”,可以通过生成格式良好的 C 代码和/或调用编译器并评估其结果来实现。 “我想使用 C 的一个子集来简化验证”的陈述原因并不能很好地支持陈述的目标。声明“我不想限制可以编写的内容,因此我想要 C 的一个子集,这样给定任何 C 程序,您都可以编写一个程序,该程序将在现代编译器中编译成相同的机器代码”是一个 不合逻辑。而后者基本上是不可能的。
  • “我想使用 C 的一个子集”和“我不想限制可以写的内容”这两种说法是矛盾的。问题结束的地方,生成相同的机器代码,与它开始的地方无关,生成将编译为 C 代码的代码。

标签: c code-generation


【解决方案1】:

您可以从“C 的子集”中排除而无需编译器输出差异的内容包括:

  • 前增量和后增量 (x++, ++x)。这包括必须明确模拟副作用的异常情况。

  • 赋值运算符(x =+ .. 等)。这包括必须明确模拟副作用的异常情况。

  • 数组访问(但不是数组的定义)或 * 作为指针取消引用(例如 x = *pointer; 替换为 x = pointer[0];)。

  • 几乎所有空格(例如,除了换行符之外的所有空格),包括 cmets 和续行符(\ 在行尾)

  • 三元组(可替换为普通字符)

  • dowhilefor(都可以用ifgoto替换);但不是switch(我希望现代编译器使用switch 关键字作为“考虑转换为跳转表”提示)

  • break;(可替换为goto

  • inline 关键字(它通常被忽略)

  • 表达式中的括号。这些可以通过重新排列表达式和/或使用临时变量(例如 x = (y + z) * 3; 替换为 temp = y + z; x = temp * 3;)来消除。

  • 一半的关系运算符(例如x = a != b可以替换为x = !(a == b);x = a <= b可以替换为x = !(a > b);等)

  • 数字常量的十六进制和八进制(可以用十进制常量代替)

  • 否定。可以用减法代替(例如x = -y;x = 0 - y;代替)

  • &|。使用 DeMorgan 定律替换为等效项(例如,x = a & b; 替换为 x = ~(~a | ~b);)。

  • &&||。使用 DeMorgan 定律替换为等效项(例如,if( (a == b) && (c == d) ) 替换为 if( !(a != b) || (c != d) ))。

  • 逗号用作语句分隔符。使用逗号作为语句分隔符的代码始终可以重构为不使用逗号分隔符的代码(例如,for(i = 0, j = 0; i < k; i++, j++) { foo(); } 替换为 j = 0; for(i = 0; i < k; i++) { foo(); j++; }i = 0; j = 0; if(i < k) goto done; next: ; foo(); i = i + 1; j = j + 1; if( !(i < k)) goto next; done: ; }

  • enum。可以换成一组#define ..

注意 1:“不需要编译器的输出有差异”(在我的第一句话中)意味着编译器的输出可能存在不需要的差异。

注 2:我“非常不喜欢”阅读执行这些操作的源代码。为了源代码的可读性(例如,调试“Idris to C”转换器的输出以查看转换器是否存在错误),我会避免执行此列表中的任何操作。

【讨论】:

  • "inline 关键字(它通常被忽略)" - 这是错误的,每个编译器确实使用它作为提示。例如,在 Clang 中,它将内联阈值设置为 487 而不是 375 (-O3) 或 75 (-Os)。
  • trigraphs and digraphs the inline keyword and register and restrict (and auto, who uses auto).
  • @StaceyGirl:请参阅关于“无需差异”的“注释 1”.. ;-)
  • @Brendan 那么我想你可以将structunionenum 添加到列表中。但我不确定这是 OP 要求的。
  • @StaceyGirl:添加了enum。我看不出你会用什么来替代struct(尤其是如果有位域)。使用struct 替代union 将不起作用(除非编译器能够确定“结构”的不同字段不会同时使用并优化回“与联合相同”,这是非常不太可能)。
猜你喜欢
  • 1970-01-01
  • 2013-03-13
  • 2010-09-10
  • 2014-09-28
  • 1970-01-01
  • 2013-01-18
  • 2017-05-08
  • 2010-11-21
  • 2013-09-11
相关资源
最近更新 更多