TODO — colgm-mlir
Design Decisions (settled)
- Static shapes only — all tensor dimensions are compile-time constants. MLIR types like
tensor<3x256x256xf32>. No ? / dynamic dims initially.
- Graph-level auto-optimization — users describe the computation graph; MLIR built-in passes (Linalg, Affine, SCF) handle tiling, fusion, and vectorization. No manual compute/schedule separation.
- C/Rust-style infix syntax — similar to the original Colgm, but computation statements redesigned for tensor DSL.
- Lexer borrowed from Colgm bootstrap (C++) — token kinds trimmed and keywords adjusted for the tensor domain.
- Target backend — MLIR → LLVM IR (via the existing
-convert-scf-to-cf, -convert-to-llvm pipeline).
Phase 0 — Project Skeleton
Phase 1 — Lexer
- Port Colgm bootstrap lexer (
lexer.cpp, lexer.h) into the project
- Slash the token enum down to what this DSL needs
- Keywords:
func, var, return, if, else, for, struct, pub, extern, true, false
- Domain keywords:
tensor, f32, f64, i32, i64 (dtype keywords)
- Literals: integer, float, string, char
- Operators:
+, -, *, /, %, ==, !=, <, >, <=, >=, =, ->, (, ), {, }, [, ], ,, ;, ., :
- Remove Colgm-specific tokens:
elsif, defer, match, foreach, forindex, =>, use, impl, union, enum, &
- Write smoke tests for the lexer (tokenize a few example
.toy / .dsl files)
Phase 2 — AST
- Define the AST node hierarchy (similar to Colgm's
ast_kind discriminated-union pattern, or a class hierarchy)
- Declarations:
func_decl, struct_decl
- Statements:
var_decl, assign, if_stmt, for_stmt, return_stmt, expr_stmt, block
- Expressions:
binary, unary, call, tensor_literal, identifier, number_literal, float_literal, index_access
- Each AST node carries a
span for error reporting
- AST dumper / pretty-printer for debugging
Phase 3 — Parser
- Recursive-descent parser (hand-written, following Colgm's parser / MLIR Toy parser pattern)
- Parse function declarations:
func name(params...) -> ret_type { body }
- Parse variable declarations:
var name = expr; or var name: type = expr;
- Parse tensor type annotations:
f32[2, 3], i32[4], f64[2, 2, 2]
- Parse expressions with precedence climbing (Pratt parser or layered recursive descent)
- Parse tensor literals:
[[1.0, 2.0], [3.0, 4.0]]
- Parse control flow:
if, for
- Error recovery strategy (at minimum: report the first error and exit, like Colgm bootstrap)
Phase 4 — MLIR Dialect Definition
Phase 5 — AST → MLIR Lowering
Phase 6 — MLIR Passes & Optimization
Phase 7 — Code Generation (via MLIR → LLVM)
Phase 8 — Operators: Batch 1 (element-wise)
Phase 9 — Operators: Batch 2 (reductions + matmul)
Phase 10 — Operators: Batch 3 (convolution & beyond)
Phase 11 — Polish & Ecosystem
Open Design Questions (to resolve as we go)
- Exact tensor literal syntax:
[[1,2],[3,4]] vs tensor([[1,2],[3,4]]) vs f32[2,2]{1,2,3,4}?
- Function-call style for ops vs operators:
a + b vs add(a, b) vs both?
- Should
reshape / transpose expose axis arguments inline or via a separate @config / annotation syntax?
- Does
for loop stay general-purpose, or should it be specialized for tensor iteration (like for i in 0..N)?