仓颉编程
Linux环境下--开发角度
源文件--目标文件--头文件--库文件-可执行文件
文件类型
仓颉源文件以 .cj 为后缀
.cj
.o C/C++ 的目标文件,
.a 静态库以 .a 结尾。
.so 态库文件以 .so
.cjo 目标文件, 类似于 C/C++ 的 obj 文件,
是一种源代码编译后生成的目标文件二进制文件,其中的 cjo 文件,应该类似于 C/C++ 的 obj 文件,是一种源代码编译后生成的目标文件
bchir2 的二进制文件
LLVM的二进制文件通常是指LLVM的中间表示(IR),也就是位码(bitcode)
.lds .lds 文件是链接脚本文件,LDS(Linker Description Script)通常在使用 GNU 链接器(ld)时使用
.bchir2
.bc 位码(bitcode)
诊断信息可读性强
编译器角度
Linux环境下--开发角度
源文件--目标文件--头文件--库文件-可执行文件
llvm 和 gcc 都是流行的编译器框架
编译器角度
1.传统的编译器通常分为三个部分,三段式
GCC 被设计为具有紧密耦合组件的单体编译器
前端(frontEnd),优化器(Optimizer)和后端(backEnd)
2. 源文件--AST--LLVM IR --可执行文件 。现代三段式
Clang
LLVM 与其它编译器最大的差别是,它不仅仅是 Compiler Collection,也是Libraries Collection
IDE 中调用 LLVM 进行实时的代码语法检查,对静态语言、动态语言的编译、优化等
更好的 IDE 支持 -被源代码分析工具和 IDE 集成
bin/opt 就是 对 IR 的优化器, IR级别做程序优化的工具,输入和输出都是同一类型的LLVM IR
bin/llc 就是 IR->ASM 的翻译,
bin/llvm-mc 就是 汇编器
汇编(llvm-as)、链接(llvm-link)
llc LLVM静态链接器
lli LLVM bitcode执行程序
llvm-as LLVM汇编器(llvm中间表示.ll到bitcode(.bc)汇编器) llvm-dis LLVM反汇编器(将llvm bitcode文件转换成可读的ir文件)
llvm-link llvm链接器
工具链
1. 一段程序的编译需要经历四个阶段(预处理—编译—汇编—链接)
预处理阶段:识别以#开头的命令,如#include,将头文件的内容插入到源文件中,生成一个预处理后的文件(通常以.i为扩展名)
编译阶段:将预处理后的文件翻译成汇编语言,生成汇编代码(通常以.s为扩展名
汇编阶段:将汇编代码翻译成机器语言指令,生成可重定位的目标文件(通常以.o为扩展名)
链接阶段:将多个目标文件和库文件链接成一个可执行文件
可重定位文件 共享的目标文件
2.执行过程
LDS(Linker Description Script)
每一个链接过程都由链接脚本(linker script, 一般以lds作为文件的后缀名) 控制
链接器把一个或多个输入文件合成一个输出文件.
输入文件: 目标文件或链接脚本文件.
输出文件: 目标文件或可执行文件.
VMA(virtual memory address虚拟内存地址或程序地址空间地址)
LMA(load memory address 加载内存地址或进程地址空间地址)
链接脚本由一系列命令组成, 每个命令由一个关键字(一般在其后紧跟相关参数)或一条对符号的赋值语句组成. 命令由分号‘;’分隔开
3. 编译器发展
GCC 编译 GCC 被构建成一个单一的静态编译器,这使得它非常难以被作为 API 并集成到其他工具中
GCC 作为前端来对用户程序进行语义分析产生 IF(Intermidiate Format),然后 LLVM 使用分析结果完成代码优化和生成
从零开始写 C、C++、Objective-C 语言的前端 Clang,完全替代掉 GCC。
Clang 是 LLVM 的前端,可以用来编译 C,C++,ObjectiveC 等语言。Clang 则是以 LLVM 为后端的一款高效易用,并且与IDE 结合很好的编译前端
C语言的编译过程
hello.c
hello.o object_file 目标文件
链接:链接需要的动态库和静态库,生成可执行文件
不同的架构,生成对应的可执行文件 main.out 可执行文件
在Win下, 动态库文件以 .dll 结尾,静态库以 .lib 结尾。
在Linux下,动态库文件以 .so 结尾,静态库以 .a 结尾。
在Mac下, 动态库文件以 .dylib 结尾,静态库以 .a 结尾
在Linux系统中,.a和.so文件是两种常见的库文件格式
库文件 libm.so
Linux系统中,动态链接库(Dynamic Linked Libraries,简称.so文件)是程序运行时需要链接的库文件
.a文件
功能:包含了一组目标文件(.o文件)的集合,这些目标文件在编译时被链接到可执行文件中,成为可执行文件的一部分
libcgicc.a:静态库;
libcgicc.so.3.2.10:动态库,主版本:3;次版本:2;修订号/发行版本:10
LLVM IR有两种文件格式.ll和.bc,.ll 文件和 .bc 文件都是 LLVM 中间表示的不同表示形式,
.ll 文件是文本形式的可读表示,方便分析和调试;
.bc 文件是二进制形式的紧凑表示,用于编译过程中的处理
轻量CFFI (Foreign Function Interface for C)
背后的事情
前端 : Objective-C 语言代码的编译器前端是Clang, Clang是LLVM的一个子项目。它是基于LLVM架构的轻量级编译器
swift代码的编译器前端是Swift,
后端:他俩的编译器后端都是llvm(这里的llvm是编译器后端的名称,不是整个LLVM架构)
Frontend 编译器前端的任务是解析源代码。
它会进行词法分析、语法分析、语义分析检查源代码是否存在错误,然后构建成语法抽象树(language-specific Abstract Syntax Tree)。
而LLVM的前端还会把AST树转换为中间代码IR(LLVM Intermedaite Representaion)
中间表达(Intermediate Representation,即 IR)编译器结构——编译器中端
存储的二进制中间语言,以.bc结尾
Backend 后端 是把优化后的代码转换为目标机器码(target instruction set
Rust 团队在其编译架构中引入了额外的一层 Mid-LevelIR(简称 MIR)
Swift 团队在其编译架构中引入了额外的一层 Swift Intermediate Language(简称 SIL)
Cangjie High-LevelIR(简称 CHIR)
Rust_Source --> HIR ---> MIR----> LLVM IR -----> MAcine code
swift_Source --->parse-AST-->Sema-->SILGen --->SIL--->Analysis-->IRGen ---->IR
CJ源码--->词法分析---->AST--->宏展开 语义分析---> AST2CHIR---->CHIR
--->CHIR---^…………^------> CHIR2LLVMIR----->LLVM IR ----->LLVM 后端工具链(/opt/llc/ld)--二进制可执行文件
保留仓颉类型的语义信息
CHIR 是针对仓颉语言设计实现的需求,在 AST和 LLVMIR 之间引入的的一层高抽象层次的 IR。
其他:
Emscripten实现把C/C++文件转成wasm,wast(wasm的可读形式),llvm字节码(bc格式),ll格式(llvm字节码的可读形式)
Rust 团队在 2016 年在其编译架构中正式引入了额外的一层
rustc 使用 LLVM 作为后端来优化并将其转换为低级机器代码
rustc 本质上是一个 LLVM 前端
传统 AST + Mid-LevelIR +IR 的两层架
CMake是一个跨平台的自动化构建系统
构建过程
C++ cmake c++构建工具的事实标准,然而其并不能算是一个合格的构建工具
Rust cargo-构建工具
Golang 自带的工具链是最常用的构建工具,包含一系列命令行工具,如go build、go test、go install和go mod等
swift Xcode是苹果官方推出的开发工具,也是开发Swift语言的首选
kotlin Gradle插件:Gradle是Kotlin官方推荐的构建工具之一 Maven
并发
Rust 在标准库中提供了操作系统级的线程模块,允许开发者可以创建和管理线程,并鼓励消息传递来实现线程间通讯
Go 语言提供 goroutine 实现了有栈的用户态协程
Swift 提供了任务(Task)表示一个独立的并发执行单元,它是用户态协程的无栈实现
仓颉
cjc 时仓颉编译器会自动进行前端、后端的编译以及链接工作
cjc-frontend 仅作为前端编译器的实体体现提供主要给编译器开发者使用
chir 为 cjc 编译器前端的一种中间表示,
GNU binutils是一个二进制工具集,默认情况下所有 Linux 发行版中都会安装这些二进制工具
语言配置
runtime 初始化可选配置 cjHeapSize 指定仓颉堆的最大值,支持单位为 kb(KB)、mb(MB)、gb(GB),
编译支持
windows、linux、macOS、hm
支持的后端有:
llvm、llvm-x86、llvm-x86_64、llvm-arm、llvm-aarch64、
cjvm、cjvm-x86、cjvm-x86_64、cjvm-arm、cjvm-aarch64
反射
TypeInfo 这一类型,这个核心类型中记录任意类型的类型信息,并且定义了方法用于获取类型信息、设置值等。
当然为了便于用户操作我们还提供了 ClassTypeInfo、
语言特性
编译时刻加载称之为静态加载,
而动态加载指的是仓颉程序可以在运行过程中通过特定函数来访问仓颉动态模块,以此读写全局变量、调用全局函数、获取类型信息的能力
编程 -严酷的攻击环境下通过遵守这一原则保证程序的执行过程符合预期结果
不能做出任何外部数据符合预期的假设,外部数据必须经过严格判断后才能使用
经典编译器
gcc的编译器 gcc的汇编器 gcc的链接器
GCC是GNU Compiler Collection 编译器集合
GCC支持一门新的语言或者目标平台会变得很困难
系统链接器
构成
Frontend:前端,词法分析、语法分析、语义分析、生成中间代码
中间层(Middle-end):负责优化中间表示的代码,以提高程序的性能和效率。
后端(Backend):将优化后的中间表示代码转换为目标平台的机器代码。
运行时库(Runtime Libraries):包括各种标准库和运行时支持库,为程序提供所需的功能和支持
目标三元组(Target Triple)是 GNU 构建系统中的核心概念,描述了一个代码运行的平台。
它们包含三个字段:machine-vendor-operatingsystem
CPU 家族 / 型号的名称、
供应商、
操作系统名称。
可以通过运行 gcc 来查看当前系统的默认目标三元组:
eg: x86_64-unknown-freebsd
x86_64-unknown-linux-gnu
LLVM(Low Level Virtual Machine)-模块化编译器工具集合
“The name “LLVM” itself is not an acronym; it is the full name of the project.”
LLVM 编译器架构 由多个子项目组成的总体项目--以C++编写而成,起源于2000年伊利诺伊大学
LLVM内核库 LLVM Core libraries
Clang:Clang是一个”LLVM原生”C/C++/Objective-C编译器
LLDB:LLDB项目是建立在LLVM和Clang上的优秀本地调试库
LLD:LLD是一个新的连接器。这是系统链接器的直接替代品,运行速度更快
重新定位的代码(Relocatable Code)。
LLVM 对交叉编译 (cross-compilation) 有原生支持,你既可以编译到你当前的机器架构上,也可以很轻松地编译到其他架构上
操作系统
Unix 的发展始于1969年
GCC 起源于1980年代早期
Linux 于1991年10月5日首次发
在Windows下体验GCC最常用的有两种方式:一是在Cygwin下使用GCC,另外一种是使用MinGW。
现代编程语言
基本内容: 基本语法 安装,运行,发布,三方包引入
该语言的基本设计思路,工程架构特点
现代编程语言的配套工具
Cangjie 仓颉为开发者提供了编译器、调试器、包管理器、静态检查工具、格式化工具和覆盖率统计工具等一整套仓颉开发工具链
Rust 工具链是一组用于编译、构建和运行 Rust 代码的工具集合。它包括编译器(rustc)、标准库以及其他相关的工具和库
纯手工开发流程,包括编译,文件创建,目录组织等等都需要自行维护,
为了方便工程化,并且快速创建工程,Rust提供了自己的自动化工具链cargo
cargo init 自动创建工程,包括基本的配置文件,main.rs生成等等
cargo build 单独编译,如果需要单独build可能是最终发布二进制程序
cargo run 自动编译并执行
crate是rust对外分发和代码共享的单位,类似jar包,类似so库,是三方库的概念 共享代码模块的公网地址为crates.io
module是模块的概念,是代码组织方式,
类似于c++的namespace, 类似于golang的package的概念
Golang
Go语言的工具链介绍 包括:
1、编译和链接工具,比如go build和go install;
2、包管理和版本控制工具,比如go get和go mod;
3、代码格式化和优化工具,比如go fmt和go vet;
4、测试和分析工具,比如go test和go bench
反汇编工具 Discover IDA Pro, the industry standard for code analysis and vulnerability research.
Powerful disassembler, debugger, and decompiler in one tool
编程语言
声明式 命令式 ,
声明式表示计算机做什么,命令式表示计算机如何做
声明式语言某种程度上来说是“更高级别”的语言;它们更符合程序员的观点,而不是实现者观点。
但是出于性能问题,命令式语言才是主流
Tokens 和 Regular Expressions Tokens 是组成程序的基本块--具有含义的最短字符串
上下文无关语法符号
scanner,parser,regular expression,以及 context-free grammar 都是基于自动化理论的形式化部分
必须与设计范式一起研究实现编程语言的策略。原因是一门语言的成功取决于实现的质量。同样,策略的选择会限制语言的设计
Programming Language Pragmatics, Fourth Edition
仓颉语言的编译过程
首先通过词法分析将代码转换成 Tokens,然后对 Tokens 进行语法解析,得到一个语法树。
每个语法树的节点可能是一个表达式、声明、类型、模式等。
仓颉 ast 库提供了每种节点对应的类,它们之间具有适当的继承关系。其中,主要的抽象类如下:
Node: 所有 语法节点 的父类
TypeNode:所有 类型节点 的父类
Expr: 所有 表达式节点 的父类
Decl: 所有 声明节点 的父类
Pattern: 所有 模式节点 的父类
keyword 关键字
identifier 标识符
assign
ast 包主要包含了仓颉源码的语法解析器和仓颉语法树节点
1.构造 Tokens
提供了 quote 表达式来从代码模版来构造 Tokens
quote 表达式是用于构造 Tokens 的一种表达式,它将括号内的程序片段转换为 Tokens
2.把Tokens 解析为相应类型的节点
parseExpr(input: Tokens): Expr:将输入的 Tokens 解析为表达式
parseDecl(input: Tokens, astKind!: String = ""):将输入的 Tokens 解析为声明,
3.查看节点的组成部分
dump 函数方便实时查看语法树节点的整体结构
该节点进行增、删、改、查等操作
TokenKind 类型
https://developer.huawei.com/consumer/cn/doc/cangjie-guides-V5/tokenkind_type-V5
Tokens 由若干个 Token 组成,一个 Tokens 代表由多个 Token 组成的序列
每个 Token 可以理解为用户可操作的词法单元,每个 Token 包含它的类型、内容和位置信息。 value kind pos
Tokens 类型支持以下函数
dump():打印包含的所有 Token,供调试使用
toString():打印 Tokens 对应的程序片段
参考
GCC与LLVM工具链 https://blog.trav.one/note/20.html
仓颉 High-Level IR 设计与实现 https://mp.weixin.qq.com/s/BHFdWMh9Blo3_6FnEjqRGQ
仓颉编程语言库 API https://cangjie-lang.cn/docs?url=%2F0.53.13%2Flibs%2Flibs_overview.html