Tag: 编译器

学习Swift背后的编译器基础架构

关于2016年LLVM开发者会议的说明 2016年美国LLVM开发人员会议于11月3日至4日在圣何塞举行。 没有惊喜。 许多演讲者和与会者都来自Apple。 我很幸运有机会参加这次会议。 讲座涵盖了LLVM,Clang,JIT,编译器工具链和构建系统。 LLVM(以前称为低级虚拟机)是模块化和可重用的编译器以及工具链技术的集合,用于开发编译器字体的前端和后端。 它也是由Swift的创建者Chris Lattner在2003年创建的。当然,Swift使用的是LLVM。 因此,如果您对Swift感兴趣,那么您肯定想了解有关LLVM的更多信息。 构建软件的新架构 演讲者:Apple Inc.的Daniel Dunbar 由于某些原因,没有人喜欢CMake。 演讲中介绍了llbuild-一个新的构建系统。 它是围绕可重用​​,灵活和可扩展的通用构建引擎设计的,该引擎能够解决许多类似于“构建系统”的问题。 它支持C,C ++和Swift。 它是Swift开源项目的一部分。 当前,Xcode,Swift操场和Swift包管理器正在使用llbuild。 对于速度,忍者仍然比llbuild快。 但是,丹尼尔(Daniel)提到llbuild在将来可能与忍者一样快。 用CMake开发和发行Clang 演讲者:苹果公司的克里斯·比尼曼 LLVM用于使用autoconf构建系统。 演讲者分享了有关他们如何在LLVM 3.9中迁移到CMake系统的故事。 他给出了CMake的几个原因: 跨平台构建配置工具 简单而强大的脚本语言 在许多平台上支持本机开发和IDE 活跃的替代开源社区。 他还指出,一个好的构建系统是没人想考虑的。 有人确实向Chris Bieneman问了一个问题:“ llbuild vs. CMake,哪个更好?” Chris Bieneman没有给出答案。 我的理解是llbuild支持Swift,而CMake不支持。 处理寄存器层次结构 演讲者:苹果公司的Matthias Braun 演讲者谈到了编译器代码生成阶段的寄存器分配器。 这是非常有趣的话题。 LLVM的一大特色是LLVM IR使用了无限的单辅助寄存器机器指令集。 在代码生成阶段,寄存器分配器确定从LLVM寄存器到物理寄存器的映射。 这简化并改善了许多编译器优化的结果。 演讲者还展示了寄存器分配器中的最新算法,可将GPU着色器程序的平均寄存器计数减少20%。 也有一些关于Clang静态分析器,调试工具和编译器优化的讨论。 其中大多数处于研究阶段,不适用于大型软件项目。 您可以在https://llvmdevelopersmeetingbay2016.sched.org上查看完整的时间表。 […]

Swift,C,LLVM编译器优化

用很少和基本的词来说,LLVM是一个编译器框架,在“前端”-“后端”多层体系结构中支持许多不同的编程语言,其中,第一层非常容易地对源代码进行解析和分类,以生成中间语言表示形式,第二层层将中间表示形式转换为针对不同处理器体系结构优化的实际组装机器代码。 当然,LLVM比这个简单的描述要多得多,但是我并不十分在意这篇文章中的LLVM。 我在这里真正关心的是专注于基于LLVM架构的Swift和Clang现代编译器如何进行编译,尤其是优化我们开发人员每天生成的源代码,尤其是这些编译器如何对某些旧式技巧,窍门做出反应和优化,我们尝试在源代码中花费一些时间。 在这篇文章中,我将测试一个在Swift和C中使用不同编程技术实现的超简单但繁琐的函数,以试图迫使编译器遵循一些经典的优化模式。 我将提供编译后代码的运行时执行性能结果,并分享生成的汇编代码中的一些优化细节。 假设您已经分配了一个很大的缓冲区,不管是在堆还是在堆栈上,我们都不在乎,我们可以很容易地说一个整数数组,我们的基本函数只需要为该数组的每个位置分配一个特定的值。 而已! 因此,在C语言中,我们的功能可能是这样的超级基本的东西: void testLoop(int64_t * buffer,int64_t tot){ for(int64_t i = 0; i <tot; i ++){ *缓冲区++ = 1; } } 在Swift中,类似的东西非常类似: func testLoop(_ a:inout [Int],_ tot:Int){ 对于i in 0 .. <tot { a [i] = 1 } } 注意,我在这里使用64位整数,因为这是64位体系结构上Swift Int的默认值,我希望能够支持很大的缓冲区以及巨大的循环。 一个好的开发人员通常可以在这种超级简单的场景中应用的基本技巧之一就是试图减少循环指令占用空间的影响。 举例来说,除了让循环具有n个交互并在每个循环交互上没有一条赋值指令,我们还可以将总的循环交互减少一个数量级,并在循环周期内放入10个这样的嵌套指令, 在C中: void testLoop(int64_t * buffer,int64_t tot){ int64_t […]

快速中级语言

Надняхна可可头-Санкт-ПетербургерассказалпроустройствокомпилятораLLVM和Swift中级语言。 Подкатомтекстовыйвариантдоклада。 Историческийликбез ПроектLLVM是2003年12月。 ДоэтогоонвтечениитрёхлетразрабатывалсявстенахИллинойскогоуниверситетакакнаучныйпрое。 苹果公司,2005年LLVM推出的Позднее,какмызнаем。 级别低的虚拟机。ИзначальнопроектLLVM。 Итаккакданноеназваниесбивалостолку,намекаянато,чтоврукахнаходитсявиртуальнаямашина,вбудущемLLVMпересталсчитатьсяакронимом – сейчасэтополноеназваниекомпилятора。 Унасестьобобщеннаясистемакоманд,прослойкаабстракцииотконечнойархитектуры,норевер Этонеболее,чемсистемакоманд,котораяможетиспользоватьсядлядостижениясвоихцелей。 АрхитектураLLVM LLVM中的应用程序—图库视频影像#132848108 LLVM。LLVM —Проскомпиляторсмодульнойархитектурой。 Онсостоитизтрехмодулей,您在каждогоизкоторыхестьсвоячеткаязонаответственности: 前端; 中端(优化程序); 后端。 中间代表(IR)。 задачей 前端’ аявляетсяпреобразованиеязыкавысокогоуровня(Swift’а),是IR; 中端 ответственензаоптимизациюэтогопромежуточногопредставления; 后端 преобразуетэтопредставлениевмашинныйкодподконкретнуюархитектуру。 Дляпреобразованиякодавнекотороепромежуточноепредставление,такжевыполняетсяцепочканекоторыхпреобразований,входекоторыхкодпредставляетсявтрехразныхпредставлениях。 Навходкомпиляторупоступаеткод,написанныйнаязыкевысокогоуровня。 迅速添加Swift。 Первыйэтапназывается 解析 。 Парсерответственензагенерациюабстрактногосинтаксическогодерева— AST( 抽象语法树 )безкакой-либосем Затемнаступаеточередьсемантическогоанализа( 语义分析 )。 Врезультатечегоформируетсяужесемантическикорректноетипизированноесинтаксическоедерево。 Наэтомэтапеопределяетсяналичиесемантическихошибок。 Наследующемшагепроисходитимпорткода,Objective-CиC,例如Swift’овоепредставление。 备注ClangImporter 。 Swift中级语言Swift中级语言вегопервойформепредставления。 ПерваяформаSILпоступаетнавходоптимизатору。 Которыйвыполняетанализ,оптимизациюитрансформациюэтойформывканоническую。 Сначаланашагетрансформациивыполняетсядиагностикапотокаданныхигарантированныепреобразования,повышающиепроизводительностьпрограммы(определяетсяиспользованиенеинициализированныхпеременных,проверяетсядостижимостьвсехузловграфапотокаданных)。 ДальшеSILOptimizerвыполняетвысокоуровневыедоменно-специфичныеоптимизациидлябазовыхтиповконтейнеров。 Такжеонответственензадевиртуализациюфункций,ARC ARCиспецификациюдженериков。 ПослеэтогоужегенерируетсяпромежуточноепредставлениеLLVM,котороепоступаетнавход后端。 Почему-томногие,когдаговорято后端LLVM,имеютввидусамLLVM。 […]

快速的构建时间重新分析和优化

表达过于复杂,无法在合理的时间内解决。 考虑将表达式分成不同的子表达式。 我认为就是这样。 解决方法 :为了缩短构建时间,只需将表达式拆分为子表达式并将其存储到变量中即可。 这将使您的编译器的工作更加轻松。 让我们继续。 三元运算符 我们再次从旧代码开始: 现在我们从实验中删除除三元运算符之外的所有内容: 编译时间 :7毫秒。 好吧,闻起来像……胜利。 结论:关于复杂表达式的一切。 经过一些研究,我发现复杂的表达只是一个基础。 但是真正的“ Evil医生”是Swift类型检查器。 我将在下一篇文章中提供更多详细信息。 大赦几 数组串联: 编译时间 :19ms 回合() //建立时间:1433.7ms 设展开= a — b — c +回合(d * 0.66)+ e 编译时间 :6 ms。 注意所有变量应该是相同的类型。 如前所述,类型检查对于Swift来说是一个痛苦。 最甜蜜的: 编译时间 :86 ms。 至少不是12小时。 它可能会更快,但我会在下一部分继续介绍它,因为它需要对编译器结构进行一些审查。 第二篇文章的最后一个惊喜是: 也可以重构为: 编译时间 :7.3 ms。 很好

CaseIterable在Swift内部如何工作

CaseIterable是我在Swift 4.2中最喜欢的功能之一。 尽管它是一个简单的协议,但它解决了一个常见问题(我个人曾多次遇到),该问题需要访问包含某个枚举的所有情况的数组。 如果我们看一下标准库中CaseIterable的实现方式,我们可以看到该协议正是人们所期望的:一系列案例的简单定义。 public protocol CaseIterable { /// A type that can represent a collection of all values of this type. associatedtype AllCases: Collection where AllCases.Element == Self /// A collection of all values of this type. static var allCases: AllCases { get } } 但是本文与该协议的Swift方面无关。 如您所知,该协议很特殊:您不需要定义和填充allCases类型- 编译器会为您完成。 enum MyEnum: CaseIterable { case foo […]

@dynamicMemberLookup如何在Swift内部进行工作(+创建自定义Swift属性)

在Swift 4.2中引入了@dynamicMemberLookup属性,以向语言中添加某种程度的动态性,类似于在Python之类的语言中。 应用于类型时,该类型的属性将在运行时解析,这意味着您可以调用未明确定义的事物,这些事物不一定存在: @dynamicMemberLookup类MyClass { 下标(dynamicMember输入:字符串)->字符串{ 返回输入==“ foo”吗? “ bar”:“ SwiftRocks” } } MyClass()。foo //酒吧 MyClass()。notFoo // SwiftRocks //这些属性不存在,但是可以调用,因为类型为@dynamicMemberLookup。 如代码片段所示,该属性的使用改为强制类型提供dynamicMember下标,该下标接收“ fake”属性名称作为参数并对其进行操作。 最初的动机是它可以与动态语言一起用于互操作性层,从而使您可以像在Python本身中一样在Swift中调用Python代码。 尽管此属性并不考虑纯粹的Swift,但您当然可以为其使用它。 我可能永远不会在常规的iOS开发中使用它,但是我最喜欢的用例是能够改善JSON解析: 让数据:字符串? = dict [“ data”]如? 串 // 让数据:字符串? = dict.data //动态搜索“数据” 以类似的方式,在Swift 5.0中添加了@dynamicCallable作为后续操作,以增加从dynamicCallable类型动态调用方法的能力: 让myType:MyDynamicType = MyDynamicType() myType(someArg2:someVal,someArg2:someVal2) 我很想了解属性在编译器内部的工作方式,以了解有关它们如何将这些假表达式转换为合法表达式的更多信息,因此我再次对Swift编译器进行了反向工程以找到这些答案,并利用了这些知识创建我自己的Swift属性。 免责声明:与往常一样,这是我自己的研究和反向工程的结果,其纯粹目的是学习新知识。 由于我显然与这些属性的原始发展无关,因此某些假设可能并不完全正确。 如果您知道编译器的工作原理,请随时纠正我! 如果您发现难以在Medium上阅读该代码,则 我的博客SwiftRocks.com上的帖子格式更好。 本文将重点介绍@dynamicMemberLookup的内部@dynamicMemberLookup 。 @dynamicCallable工作方式@dynamicCallable不同,但总体上遵循相同的想法。 快速回购搜索向我们显示,在Swift中,所有属性主要在Attr.def文件中定义。 这是@dynamicMemberLookup的定义: SIMPLE_DECL_ATTR(dynamicMemberLookup,DynamicMemberLookup, OnNominalType,9) “简单属性”是不保存数据的属性(与@available包含参数不同)。 […]

在Swift中从不内部工作

在Swift 3中添加的Never类型可以让您定义一个确保您的应用程序崩溃的方法。 尽管我们很少编写直接使用此类型的方法,但由于它是强制崩溃方法的返回类型(如fatalError() ,因此我们会不断与之交互。 这种类型带来的好处是,调用另一个Never -returning方法的方法不需要提供返回值,毕竟肯定会发生崩溃: func getSomeNumber()->整数 致命错误() //我没有返回Int,但是仍然可以编译 //因为fatalError()返回“从不”。 } 内心深处, Never只是@noreturn的经过改进的“ swifty”版本:该属性提供了完全相同的功能,但由于其性质太复杂而被删除。 将此行为连接到返回类型可以使编译器更好地工作,并且当然也可以最终为开发人员带来更好的外观。 不过,关于Never一些事情吸引了我。 我们知道语言本身没有什么可以让您跳过返回值,那么在编译器中做了什么神奇的事情呢? 就像我以前关于CaseIterable的内在特性的文章一样,我将深入研究编译器,以提取和分析可拦截并更改返回Never的方法功能的代码段,以详细了解如何Swift编译器有效。 免责声明:与往常一样,这是我自己的研究和反向工程的结果。 由于我显然与 Never 的原始开发无关 ,因此某些假设可能并不完全正确。 如果您知道编译器的工作原理,请随时纠正我! (如果您发现在Medium上难以阅读的代码,请单击此处在我的博客上阅读本文!) 期望找到复杂的代码,标准库中Never的实现向我们展示了其他东西-类型不过是一个空的枚举: 公共枚举从来没有{} 尽管这看上去确实很奇怪,但从理论上讲是正确的。 当Never实现时,Swift还在其术语中增加了无人居住类型的概念-一种没有价值的类型,通常用于表示永远不会发生的事情的不存在的结果。 由于无法以任何方式实例化没有大小写的枚举,因此可以完美地表示该概念。 但是,如果类型本身不执行任何操作,那么魔术在哪里完成呢? 在Swift回购中快速搜索”never typecheck”发现一个名为isNoReturnFunction()的方法,该方法搜索是否存在无人类型的返回: bool SILFunctionType :: isNoReturnFunction()const { for(无符号i = 0,e = getNumResults(); i <e; ++ i){ 如果(getResults()[i] .getType()-> isUninhabited()) 返回true; } 返回false; } […]