Files
quartz-research-note/content/mimiumのMIRコンパイル過程を真面目に考える.md
松浦 知也 Matsuura Tomoya e7f0f2ccd2
Some checks failed
Build / build (push) Failing after 12m58s
[obsidian] vault backup: 2025-10-01 12:02:55[
2025-10-01 12:02:55 +09:00

155 lines
4.0 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

---
date: 2024-10-20 12:02
---
#mimium
ツリー形式からBasicBlockのインストラクション形式に変える
Stateのこととupvalueを両方処理しなくてはならない。ワンパスで処理できるのか
StateSize計算とUpvalue計算の両方をtraitとして切り出す方がいいのかな
## 値
$$
\begin{align}
v \; ::= & \quad R \\
| & (\lambda x:\tau.e, [\Gamma, x:e],StateStorage(p,Vec)) \quad & [Closure]\\
\end{align}
$$
$$
\begin{align}
e \; ::=& \quad x \quad x \in \mathbb{V} \quad & [value]\\
|& \quad \lambda x.e \quad & [lambda]\\
|& \quad e \; e \quad & [appglobal,stateful)]\\
|& \quad appcls \; e \; e \quad & [appclosure]\\
|& \quad fix \; x.e \quad & [fixpoint]\\
|& \quad getstate \; e \; I_n \; I_s \quad & [feed] \\
|& \quad delay \; e \; e & [delay]\\
\end{align}
$$
結局[[The w-calculus a synchronous framework for the verified modelling of digital signal processing algorithms|W計算]]のStaged Interpreterと変わらんかもな
そうすると型付けの時点でクロージャ相当の項とグローバル関数適用の項は分かれることになる?エフェクトとして考えるのが妥当なのかな
## 型
$$
\begin{align}
\tau ::=&\quad R_a \quad & a \in \mathbb{N}\\
|&\quad I_n \quad &n \in \mathbb{N} \\\
|&\quad \tau → \tau \quad \\
% |&\quad \langle \tau \rangle
\end{align}
$$
## コンパイル
Valueとして、次のようなものがあり得る
```rust
struct VRegister(usize);//Vregisterは無限に使用できてサイズ可変
struct StackSlot(usize);
enum Value{
Static(usize),
Global(usize),
Function(usize),
ExternalItem(usize),
Register(VRegister),
StackSlot(StackSlot),//引数とかはこれで処理して良い
}
```
原則、Letが出てくればStackSlotに確保、そうでない一次変数はRegisterに確保でよい
LetだけどRegisterに逃がせるとか、RegisterからStackへスピルするとかはオプティマイザの範疇
基本的にはFuncProtoはValueに依存しない構造にしたい
今はmir::ValueにmirgenとBytecodeGen両方が依存しているが、Valueは本来Expr→MIR生成時に使われるだけの中間的な値であるべき
```rust
struct MIR{
name:Symbol,
static_data:Vec<StaticData>
externals:<ExternalItems>
functions:Vec<FuncProto>
}
struct Operation{
dest:Option<VRegister>,
inst:Instruction
}
struct UpIndex(FnId,usize)
struct UpIndexStorage(Vec<StackSlot>)
impl UpIndexStorage{
fn get_upv_index(&self,i:UpIndex)->Option<StackSlot>{
self.0.get(i.0)
}
}
struct BlockId(usize)
struct BasicBlock{
label:Symbol,
entry_v:Vec<BlockId>
op:Vec<Operation>
}
struct BlockChunk(Vec<Block>)
impl BlockChunk{ /*newtype pattern*/ }
struct FuncProto{
name: Symbol,
upindexes: UpIndexStorage,
blocks: BlockChunk,
state_tree: Vec<StateLeaf> //StateTreeは実際のデータを保持しない
meta_labels:BTreeMap<StackSlot,Symbol> //print用メタデータ
}
enum Instruction{
Load(StackSlot,Type)
Store{dest:StackSlot,src:Register,Type},
GetUpValue(UpIndex,Type),
JmpIf{cond:Register,then:BlockId,else_:BlockId,merge:BlockId},
ShiftStatePos(isize)//ツリーの子インデックスに対するカーソル移動オフセット
GetState(Type)
//その他、プリミティブな命令はfrom Register to Register
}
```
評価をしながら副作用としてMIRを書き込んでいくような感じ
```rust
fn eval(e:ExprNodeId,env:Env<(Value,Type)>)->(Value,Type){
match e.to_expr() {
Expr::Id(name)=>{
match env.lookup(name){
(Value::Register(r),t)=>(r,t)
(Value::StackSlot(s),t)=>push_inst(Instruction::Load(s,t))
}
},
Expr::Let(name,e,body)=>{
let env = env.extend((name,eval(e,env).0));
eval(e,env)
},
Expr::App(e,args)=>{
let (f,ft) = eval(e,env);
let argvs = args.iter().map(|e| eval(e,env));
match f{
Value::Closure(fproto,names,body,env)=>{
let kvs = names.iter().zip(argvs.zip()).collect()
let env = env.extend(kvs);
eval(body,env)
}
_=>panic!()
}
}
Expr::Feed(id,body)=>{
}
}
}
```