305 lines
8.1 KiB
Markdown
305 lines
8.1 KiB
Markdown
# 08 - C 语言操作
|
||
|
||
Viper 通过 `c` 模块提供对 C 语言底层特性的访问,包括指针操作、内联汇编、预处理指令等。
|
||
|
||
## 指针操作
|
||
|
||
### 取地址 `c.Addr`
|
||
|
||
```python
|
||
x: t.CInt = 42
|
||
p: t.CInt | t.CPtr = c.Addr(x) # int* p = &x;
|
||
```
|
||
|
||
对结构体成员取地址:
|
||
|
||
```python
|
||
res: t.CInt = fat32.opendir("/", c.Addr(dp)) # res = fat32_opendir("/", &dp);
|
||
```
|
||
|
||
对数组取地址:
|
||
|
||
```python
|
||
viperlib.snprintf(c.Addr(buf), 64, "hello %d", 42) # snprintf(&buf, 64, "hello %d", 42);
|
||
```
|
||
|
||
### 解引用 `c.Deref`
|
||
|
||
```python
|
||
ptr: Sheet | t.CPtr = c.Addr(sheet_obj)
|
||
obj: Sheet = c.Deref(ptr) # struct Sheet obj = *ptr;
|
||
```
|
||
|
||
### 内存拷贝 `c.Load`
|
||
|
||
```python
|
||
c.Load(ptr, value) # *ptr = *value;
|
||
```
|
||
|
||
### 解引用赋值 `c.DerefAs`
|
||
|
||
```python
|
||
c.DerefAs(ptr, value) # *ptr = value;
|
||
```
|
||
|
||
|
||
### 赋值 `c.Set`
|
||
|
||
```python
|
||
c.Set(target, value) # target = value;
|
||
```
|
||
|
||
## 内联汇编 `c.Asm`
|
||
|
||
Viper 提供了声明式的内联汇编语法,编译为 GCC 风格的 `__asm__ __volatile__` 语句。
|
||
|
||
### 基本用法
|
||
|
||
```python
|
||
c.Asm("nop") # __asm__ __volatile__("nop");
|
||
```
|
||
|
||
### 带操作数的汇编
|
||
|
||
使用 f-string 和 `c.AsmInp` / `c.AsmOut` 标记操作数:
|
||
|
||
```python
|
||
saved_rdi: t.CUnsignedLong
|
||
c.Asm(f"mov {c.AsmOut(saved_rdi, t.ASM_DESCR.OUTPUT_REG)}, rdi",
|
||
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RDI])
|
||
```
|
||
|
||
等价 C 代码:
|
||
```c
|
||
__asm__ __volatile__(
|
||
"mov %0, rdi"
|
||
: "=r"(saved_rdi)
|
||
:
|
||
: "memory", "rdi"
|
||
);
|
||
```
|
||
|
||
### 输入操作数 `c.AsmInp`
|
||
|
||
```python
|
||
msg: t.CConst | t.CChar | t.CPtr = "Hello"
|
||
c.Asm(f"mov rdi, {c.AsmInp(msg, t.ASM_DESCR.REG_ANY)}",
|
||
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX])
|
||
```
|
||
|
||
### 完整示例
|
||
|
||
```python
|
||
c.Asm(f"""mov rdi, {c.AsmInp(msg, t.ASM_DESCR.REG_ANY)}
|
||
call {c.AsmInp(log_info_fn, t.ASM_DESCR.REG_ANY)}""",
|
||
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX,
|
||
t.ASM_DESCR.CLOBBER_RCX, t.ASM_DESCR.CLOBBER_RDX,
|
||
t.ASM_DESCR.CLOBBER_RDI, t.ASM_DESCR.CLOBBER_RSI,
|
||
t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9,
|
||
t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11])
|
||
```
|
||
|
||
### ASM_DESCR 约束字符
|
||
|
||
#### 操作数修饰符
|
||
|
||
| 常量 | 值 | 说明 |
|
||
|------|----|------|
|
||
| `MODIFIER_OUTPUT` | `=` | 输出操作数 |
|
||
| `MODIFIER_READWRITE` | `+` | 读写操作数 |
|
||
| `MODIFIER_INPUT` | `` | 输入操作数(默认) |
|
||
| `MODIFIER_GLOBAL` | `&` | 全局操作数 |
|
||
|
||
#### 寄存器约束
|
||
|
||
| 常量 | 值 | 说明 |
|
||
|------|----|------|
|
||
| `REG_ANY` | `r` | 任何通用寄存器 |
|
||
| `REG_EAX` / `REG_RAX` | `a` | EAX/RAX |
|
||
| `REG_EBX` / `REG_RBX` | `b` | EBX/RBX |
|
||
| `REG_ECX` / `REG_RCX` | `c` | ECX/RCX |
|
||
| `REG_EDX` / `REG_RDX` | `d` | EDX/RDX |
|
||
| `REG_ESI` / `REG_RSI` | `S` | ESI/RSI |
|
||
| `REG_EDI` / `REG_RDI` | `D` | EDI/RDI |
|
||
| `REG_XMM` | `x` | XMM 寄存器 |
|
||
|
||
#### 内存与立即数
|
||
|
||
| 常量 | 值 | 说明 |
|
||
|------|----|------|
|
||
| `MEMORY` | `m` | 内存操作数 |
|
||
| `IMMEDIATE` | `i` | 立即数 |
|
||
| `ANY` | `g` | 通用寄存器/内存/立即数 |
|
||
|
||
#### 预定义组合约束
|
||
|
||
| 常量 | 值 | 说明 |
|
||
|------|----|------|
|
||
| `OUTPUT_REG` | `=r` | 输出,通用寄存器 |
|
||
| `OUTPUT_MEM` | `=m` | 输出,内存 |
|
||
| `OUTPUT_EAX` | `=a` | 输出,EAX/RAX |
|
||
| `INPUT_REG` | `r` | 输入,通用寄存器 |
|
||
| `INPUT_MEM` | `m` | 输入,内存 |
|
||
| `INPUT_IMM` | `i` | 输入,立即数 |
|
||
|
||
#### 破坏描述符
|
||
|
||
| 常量 | 说明 |
|
||
|------|------|
|
||
| `CLOBBER_EAX` / `CLOBBER_RAX` | 破坏 EAX/RAX |
|
||
| `CLOBBER_EBX` / `CLOBBER_RBX` | 破坏 EBX/RBX |
|
||
| `CLOBBER_ECX` / `CLOBBER_RCX` | 破坏 ECX/RCX |
|
||
| `CLOBBER_EDX` / `CLOBBER_RDX` | 破坏 EDX/RDX |
|
||
| `CLOBBER_ESI` / `CLOBBER_RSI` | 破坏 ESI/RSI |
|
||
| `CLOBBER_EDI` / `CLOBBER_RDI` | 破坏 EDI/RDI |
|
||
| `CLOBBER_CC` | 破坏条件码(标志寄存器) |
|
||
| `CLOBBER_MEMORY` | 破坏内存 |
|
||
| `CLOBBER_R8` ~ `CLOBBER_R15` | 破坏 R8~R15 |
|
||
|
||
## 预处理指令
|
||
|
||
Viper 通过 `c` 模块的函数调用实现 C 预处理指令。
|
||
|
||
### #define
|
||
|
||
```python
|
||
c.CDefine("MAX_SIZE", 1024) # #define MAX_SIZE 1024
|
||
```
|
||
|
||
上述方法容易引起未定义行为,至少是不便于理解,更常用的方式是使用类型注解:
|
||
|
||
```python
|
||
MAX_SIZE: t.CDefine = 1024 # #define MAX_SIZE 1024
|
||
```
|
||
|
||
### 条件编译
|
||
|
||
```python
|
||
c.CIfndef(HEADER_H) # #ifndef HEADER_H
|
||
c.CDefine(HEADER_H) # #define HEADER_H
|
||
c.CEndif() # #endif
|
||
```
|
||
|
||
```python
|
||
c.CIfdef(DEBUG) # #ifdef DEBUG
|
||
c.CEndif() # #endif
|
||
```
|
||
|
||
```python
|
||
c.CIf(VERSION > 2) # #if VERSION > 2
|
||
c.CElif(VERSION > 1) # #elif VERSION > 1
|
||
c.CElse() # #else
|
||
c.CEndif() # #endif
|
||
```
|
||
|
||
### #undef
|
||
|
||
```python
|
||
c.Undef(MACRO_NAME) # #undef MACRO_NAME
|
||
```
|
||
|
||
### #error
|
||
|
||
```python
|
||
c.CError("Platform not supported") # #error "Platform not supported"
|
||
```
|
||
|
||
### #pragma
|
||
> 此关键字已不再支持
|
||
```python
|
||
c.CPragma("GCC diagnostic push") # #pragma GCC diagnostic push
|
||
```
|
||
|
||
### ## 连接符
|
||
> 此方法可能已经不再支持
|
||
```python
|
||
c.TokenPast("PREFIX_", "NAME") # PREFIX_ ## NAME
|
||
```
|
||
|
||
由于编译器设计,以及不需要像 `C` 一样做大量字符替换,对于上述所有的条件宏都远比 `C` 差,实际工程中不推荐使用条件宏,后期将完善宏的设计,引入模板等。
|
||
|
||
## FFI 外部函数声明
|
||
|
||
Viper 的 FFI(Foreign Function Interface)**无需任何特殊语法**——仅靠返回类型注解中的 `t.State` 标记即可声明外部函数。`t.State` 是声明性标记类型,表示"仅声明不定义"(语义等价于 `t.CExport | t.CExtern`),编译器只生成 `declare` 原型,不生成 `define` 函数体,由链接器在外部库中解析符号。
|
||
|
||
### 基本声明
|
||
|
||
函数体为 `pass`,返回类型用 `RetType | t.State`:
|
||
|
||
```python
|
||
def isr0() -> t.CVoid | t.State: pass # extern void isr0(); — 来自汇编或外部
|
||
def getchar() -> t.CInt | t.State: pass # extern int getchar(); — 来自 libc
|
||
```
|
||
|
||
编译为 LLVM IR:
|
||
|
||
```llvm
|
||
declare void @"isr0"() ; 不加 SHA1 前缀(t.State 含 CExport 语义)
|
||
declare i32 @"getchar"()
|
||
```
|
||
|
||
### Win32 API 绑定示例
|
||
|
||
[includes/w32](../includes/w32) 中的 Win32 绑定全部通过 `t.State` 声明,零特殊语法:
|
||
|
||
```python
|
||
def CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: ULONG,
|
||
dwShareMode: ULONG,
|
||
lpSecurityAttributes: SECURITY_ATTRIBUTES | t.CPtr,
|
||
dwCreationDisposition: ULONG,
|
||
dwFlagsAndAttributes: ULONG,
|
||
hTemplateFile: HANDLE) -> HANDLE | t.State:
|
||
pass
|
||
```
|
||
|
||
等价 C 声明:
|
||
|
||
```c
|
||
HANDLE CreateFileA(LPCSTR, ULONG, ULONG, SECURITY_ATTRIBUTES*,
|
||
ULONG, ULONG, HANDLE);
|
||
```
|
||
|
||
链接时通过 `project.json` 指定外部库(如 `kernel32.lib`、`user32.lib`),链接器解析这些符号。
|
||
|
||
### `t.State` 与 `t.CExtern` 的区别
|
||
|
||
| 修饰 | SHA1 前缀 | 生成内容 | 用途 |
|
||
|------|----------|---------|------|
|
||
| 无修饰 | ✅ 加前缀 | `declare` + `define` | 模块私有函数 |
|
||
| `t.CExtern` | ❌ 不加 | `declare`(引用外部) | 引用外部 C 函数 |
|
||
| `t.CExport` | ❌ 不加 | `declare` + `define` | 对外暴露 API |
|
||
| `t.State` | ❌ 不加 | 仅 `declare` | FFI 外部函数声明(= `CExport \| CExtern`) |
|
||
|
||
### 声明外部全局变量
|
||
|
||
外部全局变量通过 `t.CExtern` 注解声明:
|
||
|
||
```python
|
||
errno: t.CExtern | t.CInt # extern int errno;
|
||
```
|
||
|
||
详见 [02-type-system.md 的 t.State 章节](02-type-system.md#tstate-声明性标记) 和 [includes/w32](../includes/w32) 的 Win32 绑定实现。
|
||
|
||
## LLVM IR 内联
|
||
|
||
### c.LLVMIR
|
||
|
||
直接嵌入 LLVM IR 指令来实现高效且跨平台的汇编操作:
|
||
|
||
```python
|
||
c.LLVMIR(f"add i32 {c.LInp(a)}, {c.LInp(b)}", t.CInt)
|
||
```
|
||
|
||
### c.LInp / c.LOut
|
||
|
||
标记 LLVM IR 的输入/输出操作数:
|
||
|
||
```python
|
||
c.LInp(expr) # 输入操作数
|
||
c.LOut(expr) # 输出操作数
|
||
```
|
||
|
||
## 运算符重载
|
||
|
||
`@t.Object` 类支持运算符重载,详见 [06-oop.md 中 运算符重载](06-oop.md#运算符重载)。
|