标签 LLVM 下的文章

Firefox 版本号到了 100 会发生什么

根据 Chrome 和 Firefox 的官方时间表,Chrome 100 稳定版将于 2022 年 3 月正式发布 ;Firefox 100 将于 2022 年 3 月进入 Nightly 版本。Mozilla 担心浏览器的 User-Agent 字符串带有三位数的 Firefox 版本号可能会破坏许多网站的功能。为此他们在 Firefox Nightly 92 版本更新中进行了实验,在该字符串中使用 100 这个数字进行测试:Mozilla/5.0 (Windows NT 10.0; rv:100.0) Gecko/20100101 Firfox 100.0。开发人员需要检查他们的网站代码,以确保 Firefox 100 不被当成 Firefox 10 或 Firefox 00。

开源软件的“谦逊”传统被抛弃了,不知道从谁开始,一年不换几个大版本号就感觉没做什么似的。

Google 正计划为 Fuchsia OS 安装完整的 Chrome

Fuchsia 项目和 Chromium 项目有很多重叠之处,并与 Chrome OS 共享代码和硬件。时至今日,Fuchsia 项目依然利用 Chromium 引擎。根据 Chromium BUG 追踪器,自今年 5 月开始,Google 就开始为 Fuchsia OS “创建 Chrome 浏览器安装包”,也就是说 Fuchsia OS 将会获得完整的“Google Chrome”浏览器体验,支持包括同步等关键功能。

这样折腾,不如两个系统合并起来好了。

英特尔 C/C++ 编译器全面采用 LLVM 技术

英特尔的下一代 C/C++ 编译器正在全面采用 LLVM 编译器堆栈,以取代他们以前的专有编译器代码库。英特尔的编译器专家称,最新的英特尔 C/C++ 编译器使用 LLVM 可以提供更快的编译时间、更好的优化、增强的标准支持,以及对 GPU 和 FPGA 卸载的支持。英特尔建议所有新项目使用他们基于 LLVM 的英特尔 C/C++ 编译器,现有项目应计划在今年迁移到新编译器。他们的“经典”英特尔 C/C++ 编译器将在定期更新结束后过渡到遗留模式。除此以外,他们也在将 Fortran 编译器过渡到 LLVM。

LLVM 发展迅速,GCC 似乎有些落伍了,这是 GPL 的原因吗?

LLVM 12 已释出 3 天,但是连发布公告都没写完

LLVM 编译器套装项目是一个已经有 18 年历史的重要项目,其更新也算活跃,在很多方面都已经超过了 GCC。4 月 14 日,LLVM 释出了最新的 12.0.0。主要新特性和变化可以参见发布公告。然而,发布公告还没编写完。

没料到 LLVM 在文档更新方面居然拖拖拉拉的,这是觉得没人看么,还是懂的人自然懂?

Firefox 88 将停止支持 FTP

Mozilla 是在去年宣布它计划从浏览器中移除内置的 FTP 实现。即在 4 月 19 日释出的 Firefox 88 将关闭 FTP 实现,在 Firefox 90 将移除 FTP。在停止支持之后,浏览器将会用外部应用打开 ftp:// 链接。

在 Chrome 之后,Firefox 也将停止支持 FTP 协议。这种老旧的网络协议其实是该逐渐淘汰了,虽然更老的协议如 TFTP 依旧在很狭窄的场景中有使用。

近三万家企业使用的代码覆盖量软件陷入软件供应链黑客事件

Codecov 被认为是科技行业衡量代码覆盖率的首选厂商。该公司的工具可以帮助开发人员了解和测量测试套件执行的代码行数,并被广泛部署在大型科技企业的开发管道中。该公司宣称,有超过 29000 家企业使用其代码覆盖率洞察力来检查代码质量和维护代码覆盖率。

该公司表示,该黑客事件发生在四个月前,但直到 2021 年 4 月 1 日才被 Codecov 的一位客户在野发现。其 Bash Uploader 脚本的被替换版本可能导致任何通过 CI 运行器传递的凭证、令牌或密钥被泄露。

软件行业越来越全球化,形成了一条条供应链,在这个链条中任何一个环节出现的问题,都可能通过链条进行放大。对供应链的管理,需要更有预防和响应机制。

泄露的 Windows XP 代码经用户编译为可运行的系统

上周在 4chan 网上泄露的 Windows XP 和 Windows Server 2003 源代码,在一位用户将代码编译成可运行的操作系统后,已被证实是真实的。NTDEV 是上周下载代码的数百万用户之一,他决定编译代码,自己去判断代码真假。根据网上分享的视频,这位业余 IT 技术人员在周末成功编译了 Windows XP 代码,昨天又成功编译了 Windows Server 2003。

来源:zdnet

拍一拍:这种实锤方法令人惊叹!

Khronos 开源新 OpenCL SDK,并发布 OpenCL 3.0 规范

OpenCL 3.0 主要变化还是让 OpenCL 2 的功能成为可选,所以 OpenCL 3.0 适合更多的设备和环境,但也增加了新的扩展,围绕着能够在给定的 OpenCL 驱动/设备上获得 UUID,还增加了异步 DMA 功能,有助于 OpenCL 3.0 对嵌入式处理器的工作。

来源:cnbeta

拍一拍:以防你不知道,OpenCL 是第一个面向异构系统通用目的并行编程的开放式、免费标准,也是一个统一的编程环境。

开发者再次尝试让摩托罗拉 68000 系列进入 LLVM 上游

摩托罗拉 68000 系列(M68k)处理器在 80 年代就已经出现了,这要归功于早期苹果 Macintosh 电脑。到 2020 年,M68k 仍然是复古电脑爱好者和业余爱好者的热门目标。这已经不是第一次提出对 LLVM 的 M68k 支持了。

来源:phoronix

拍一拍:这是一种计算机考古需要。

简单说来,一个 编译器 compiler 不过是一个可以翻译其他程序的程序。传统的编译器可以把源代码翻译成你的计算机能够理解的可执行机器代码。(一些编译器将源代码翻译成别的程序语言,这样的编译器称为源到源翻译器或 转化器 transpilers 。)LLVM 是一个广泛使用的编译器项目,包含许多模块化的编译工具。

传统的编译器设计包含三个部分:

  • 前端 Frontend 将源代码翻译为 中间表示 intermediate representation (IR)* 。clang 是 LLVM 中用于 C 家族语言的前端工具。
  • 优化器 Optimizer 分析 IR 然后将其转化为更高效的形式。opt 是 LLVM 的优化工具。
  • 后端 Backend 通过将 IR 映射到目标硬件指令集从而生成机器代码。llc 是 LLVM 的后端工具。

注:LLVM 的 IR 是一种和汇编类似的低级语言。然而,它抽离了特定硬件信息。

Hello, Compiler

下面是一个打印 “Hello, Compiler!” 到标准输出的简单 C 程序。C 语法是人类可读的,但是计算机却不能理解,不知道该程序要干什么。我将通过三个编译阶段使该程序变成机器可执行的程序。

// compile_me.c
// Wave to the compiler. The world can wait.

#include <stdio.h>

int main() {
  printf("Hello, Compiler!\n");
  return 0;
}

前端

正如我在上面所提到的,clang 是 LLVM 中用于 C 家族语言的前端工具。Clang 包含 C 预处理器 C preprocessor 词法分析器 lexer 语法解析器 parser 语义分析器 semantic analyzer IR 生成器 IR generator

C 预处理器在将源程序翻译成 IR 前修改源程序。预处理器处理外部包含文件,比如上面的 #include <stdio.h>。 它将会把这一行替换为 stdio.h C 标准库文件的完整内容,其中包含 printf 函数的声明。

通过运行下面的命令来查看预处理步骤的输出:

clang -E compile_me.c -o preprocessed.i

词法分析器(或 扫描器 scanner 分词器 tokenizer )将一串字符转化为一串单词。每一个单词或 记号 token ,被归并到五种语法类别之一:标点符号、关键字、标识符、文字或注释。

compile\_me.c 的分词过程:

语法分析器确定源程序中的单词流是否组成了合法的句子。在分析记号流的语法后,它会输出一个 抽象语法树 abstract syntax tree (AST)。Clang 的 AST 中的节点表示声明、语句和类型。

compile\_me.c 的语法树:

语义分析器会遍历抽象语法树,从而确定代码语句是否有正确意义。这个阶段会检查类型错误。如果 compile_me.c 的 main 函数返回 "zero"而不是 0, 那么语义分析器将会抛出一个错误,因为 "zero" 不是 int 类型。

IR 生成器将抽象语法树翻译为 IR。

对 compile\_me.c 运行 clang 来生成 LLVM IR:

clang -S -emit-llvm -o llvm_ir.ll compile_me.c

llvm_ir.ll 中的 main 函数:

; llvm_ir.ll
@.str = private unnamed_addr constant [18 x i8] c"Hello, Compiler!\0A\00", align 1

define i32 @main() {
  %1 = alloca i32, align 4 ; <- memory allocated on the stack
  store i32 0, i32* %1, align 4
  %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @.str, i32 0, i32 0))
  ret i32 0
}

declare i32 @printf(i8*, ...)

优化程序

优化程序的工作是基于其对程序的运行时行为的理解来提高代码效率。优化程序将 IR 作为输入,然后生成改进后的 IR 作为输出。LLVM 的优化工具 opt 将会通过标记 -O2(大写字母 o,数字 2)来优化处理器速度,通过标记 Os(大写字母 o,小写字母 s)来减少指令数目。

看一看上面的前端工具生成的 LLVM IR 代码和运行下面的命令生成的结果之间的区别:

opt -O2 -S llvm_ir.ll -o optimized.ll

optimized.ll 中的 main 函数:

optimized.ll

@str = private unnamed_addr constant [17 x i8] c"Hello, Compiler!\00"

define i32 @main() {
  %puts = tail call i32 @puts(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @str, i64 0, i64 0))
  ret i32 0
}

declare i32 @puts(i8* nocapture readonly)

优化后的版本中, main 函数没有在栈中分配内存,因为它不使用任何内存。优化后的代码中调用 puts 函数而不是 printf 函数,因为程序中并没有使用 printf 函数的格式化功能。

当然,优化程序不仅仅知道何时可以把 printf 函数用 puts 函数代替。优化程序也能展开循环并内联简单计算的结果。考虑下面的程序,它将两个整数相加并打印出结果。

// add.c
#include <stdio.h>

int main() {
  int a = 5, b = 10, c = a + b;
  printf("%i + %i = %i\n", a, b, c);
}

下面是未优化的 LLVM IR:

@.str = private unnamed_addr constant [14 x i8] c"%i + %i = %i\0A\00", align 1

define i32 @main() {
  %1 = alloca i32, align 4 ; <- allocate stack space for var a
  %2 = alloca i32, align 4 ; <- allocate stack space for var b
  %3 = alloca i32, align 4 ; <- allocate stack space for var c
  store i32 5, i32* %1, align 4  ; <- store 5 at memory location %1
  store i32 10, i32* %2, align 4 ; <- store 10 at memory location %2
  %4 = load i32, i32* %1, align 4 ; <- load the value at memory address %1 into register %4
  %5 = load i32, i32* %2, align 4 ; <- load the value at memory address %2 into register %5
  %6 = add nsw i32 %4, %5 ; <- add the values in registers %4 and %5\. put the result in register %6
  store i32 %6, i32* %3, align 4 ; <- put the value of register %6 into memory address %3
  %7 = load i32, i32* %1, align 4 ; <- load the value at memory address %1 into register %7
  %8 = load i32, i32* %2, align 4 ; <- load the value at memory address %2 into register %8
  %9 = load i32, i32* %3, align 4 ; <- load the value at memory address %3 into register %9
  %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i32 0, i32 0), i32 %7, i32 %8, i32 %9)
  ret i32 0
}

declare i32 @printf(i8*, ...)

下面是优化后的 LLVM IR:

@.str = private unnamed_addr constant [14 x i8] c"%i + %i = %i\0A\00", align 1

define i32 @main() {
  %1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0), i32 5, i32 10, i32 15)
  ret i32 0
}

declare i32 @printf(i8* nocapture readonly, ...)

优化后的 main 函数本质上是未优化版本的第 17 行和 18 行,伴有变量值内联。opt 计算加法,因为所有的变量都是常数。很酷吧,对不对?

后端

LLVM 的后端工具是 llc。它分三个阶段将 LLVM IR 作为输入生成机器代码。

  • 指令选择是将 IR 指令映射到目标机器的指令集。这个步骤使用虚拟寄存器的无限名字空间。
  • 寄存器分配是将虚拟寄存器映射到目标体系结构的实际寄存器。我的 CPU 是 x86 结构,它只有 16 个寄存器。然而,编译器将会尽可能少的使用寄存器。
  • 指令安排是重排操作,从而反映出目标机器的性能约束。

运行下面这个命令将会产生一些机器代码:

llc -o compiled-assembly.s optimized.ll
_main:
    pushq   %rbp
    movq    %rsp, %rbp
    leaq    L_str(%rip), %rdi
    callq   _puts
    xorl    %eax, %eax
    popq    %rbp
    retq
L_str:
    .asciz  "Hello, Compiler!"

这个程序是 x86 汇编语言,它是计算机所说的语言,并具有人类可读语法。某些人最后也许能理解我。


相关资源:

  1. 设计一个编译器
  2. 开始探索 LLVM 核心库

(题图:deviantart.net)


via: https://nicoleorchard.com/blog/compilers

作者:Nicole Orchard 译者:ucasFL 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

GCC邮件列表中在争论GCC是否应该接受收费插件,但是认为GCC是一个自由软件(free software)开发的媒介的论调占得了上风

Gcc以及它在模块化方面的缺失又一次作为一个问题被提出来,并且和市场上的新的编译器LLVM做了对比。GCC巨大而古老:5百万行代码,30年研发时间,并且还在继续增长。相比较而言,LLVM更加年轻,更加模块化,并且允许所有的语言都作为一个模块添加进去。

LLVM的核心是‘开放源代码(Open Source)’。GCC是反著作权(copyleft)代表,是严格的自由软件(free software),她不允许以任何形式收费的插件的代码进入到GCC的代码中。争论的一种意见,正如Eric Raymond说的,“FSF不可能既阻止持有所有权的供应者添加他们的插件到一个免费编译器中,又让这个编译器得到发展。就像马儿已经偏离了跑道,反对插件策略的战略目标已经彻底的失败了”。

LLVM已经被苹果公司采用作为OS X和苹果硬件上GCC的替代品,并且正在变得流行起来,特别是在BSD系列操作系统的用户中间。LLVM的拥护者推测LLVM将会在更广阔的应用程序和移动设备开发市场上成为GCC的替代者。GCC的反对者们的观点是GCC太过复杂,并且开发者们必须遵守她的‘反著作权(copyleft)’。这限制了那些不想在‘反著作权(copyleft)’许可证下发布他们的语言或者软件产品。作为典型,苹果公司有一个很长的厌恶自由软件的历史。他们也不允许遵守‘反著作权(copyleft)’的软件通过他们的App Store发布。

LLVM和GCC之间的争论其实是GNU/Linux和BSD系列、开放源代码和自由软件之间历史差异的翻新版。开放源码的开发者允许代码被以任何形式的使用,免费或者维持版权。自由软件则严格地规定,代码或者针对代码做的更新,必须保持永久免费。自由软件的支持者认为完整的‘反著作权’授权有助于GCC的发展,并且已经将Linux和自由软件带到一个其他方式无法到达的高度,同时保证了自由软件不会被收购或者堕落成商业利益。开放源码的支持者则认为开放源码更加的自由,因为使用这没有受到限制,他们可以随意使用,包括开发非开源的版本。

GNU编译器集合(GCC)一直是自由软件发展的关键。编译器是稀有且昂贵的商品,版权软件公司也充斥着对不符合标准的特性的需求。让软件兼容不同的机器和操作系统是一个非必需的复杂任务。GCC作为第一个真正免费的跨平台编译器,简化了这个过程。

GCC对于软件开发者和移动设备开发者来说也是一个划时代的产品,而不仅仅对于那些自由软件概念提出者。GCC不但免费和可移植,她跨越不同硬件架构的普遍性和公用性使得更加容易做到软件的兼容性、鲁棒性和一致性。这和John Gilmore,Michael Tiemann和David Henkel-Wallace在开发GCC时发现的一样。这也是Cygnus Solutions公司主要的卖点,Cygnus Solutions是第一家靠卖自由软件赚钱的公司。[译注:Cygnus Solutions是John Gilmore, Michael Tiemann and David Henkel- Wallace创办的公司,同时也是GNU几个主要产品的贡献者]

LLVM和GCC之间主要的技术差异集中在组成‘前端’,‘中端’,‘后端’的模块分割。‘前端’用来翻译特定的语言。‘中端’对翻译后产生的代码进行优化。‘后端’将优化后的代码转化成特定硬件架构的机器码。LLVM将这些模块分割成不同的实体,但是由于语义的和历史的原因,GCC模糊了这些模块之间的界限。

对于一个自由软件项目,添加一种新的语言或者架构到GCC也许是一个非常困难的过程,添加有版权的插件也是不允许的。由于模块间界限非常不明确,最容易的添加方法就是让添加的特性遵循自由软件许可证。最初的开发者也许想保持代码的封闭和版权,但最后不得不将代码以自由软件发布。早期的C++以及Objective C就被认为是其中典型的例子。

与此相反,LLVM允许,甚至也许可以说是鼓励添加和发展版权语言和架构,比如英伟达基于Clang和LLVM的对于GPU开发的NVCC。NVCC的源代码是自由软件或者开源软件开发者获取不到的。

Richard Stallman对这方面的演讲中旗帜鲜明地宣布:“在自由软件运动中,我们为自由而战。自由软件的的价值观从根本上就和开源软件不同,后者以写‘更好的代码’为终极目标。如果GCC从免费的编译器变成非免费的编译器,她将不再能够达成自由的目标。

“Clang和LLVM的开发者不认可我们的价值观和目标,所以得出了跟我们不一样的结论。他们反对我们采取的捍卫自由的措施,因为他们只看到这对他们造成的不便,却没有看到(或者不关心)他们真正的需求。我猜测他们把他们的工作定义为‘开源’,并且漠视自由。”

GCC开发者们不可能在许可证的条件上妥协。LLVM在某些行业的部门非常流行,因为它很年轻很新,在编程语言的浪潮中跳跃式发展着。流行的风向着更加开放奔跑,GCC决心跟商业利益死磕也许是这个长期演进路上的一大助力。Unix公司们从80和90年代的Unix战争中学到了一些东西。语言和操作系统都是工具,它们最好是开放和共享的。GCC是自由软件,不属于任何人。


via: http://www.linuxuser.co.uk/features/staying-free-should-gcc-allow-non-free-plug-ins

译者:love\_daisy\_love 校对:wxy

本文由 LCTT 原创翻译,Linux中国 荣誉推出