Files
TransPyC/wiki/08-c-operations.md
2026-07-18 19:25:40 +08:00

305 lines
8.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 的 FFIForeign 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#运算符重载)。