es-ude / es-ude/OnDeviceTraining
Implement ir2c — C code generation from annotated IR
- Dominant language
- C
- Stars
- 1
- Forks
- 3
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 8
Description
## Context
Sub-issue of #58.
**Blocked by:** #60 (training providers), #61 (inference providers)
**Blocks:** #64 (E2E tests)
## Goal
Generate compilable C code from annotated IR that integrates with the existing C training framework.
## Open Design Decision: Jinja2 vs C-AST
**Decision deferred** until #60 and #61 are complete. The providers produce IR graphs — ir2c consumes them. The generation approach doesn't affect the provider API.
### Option A — Jinja2 Templates
- `.c.j2` templates that look like C with `{{ placeholders }}`
- IR graph flattened to template context dict → rendered to text
- Pro: low barrier, readable templates, fast to prototype
- Con: graph structure lost, no validation before rendering, hard to do transformations
### Option B — Minimal C-AST
- IR graph nodes mapped to C-AST nodes (declarations, calls, loops, assignments)
- AST validated → serialized to C text
- Pro: natural mapping from IR graph (already nodes/edges), enables validation and future transformations (layer fusion, memory scheduling)
- Con: more upfront effort (~200 lines AST definition)
### Spike Phase
Before committing to either approach, implement the same small example (Linear → ReLU → Linear → Softmax training loop) with **both approaches**. Compare:
- Lines of code
- Readability of the generation logic
- Ease of adding a new layer type
- Quality of error messages on invalid IR
## Output Strategy
**v1: single file** — matches existing `MnistExperiment.c` pattern.
Generation internally modular from day 1:
```python
def generate_model_init(ir_graph) -> str: ...
def generate_training_loop(ir_graph) -> str: ...
def generate_config(ir_graph) -> str: ...
def generate_main(ir_graph) -> str: ...
```
Splitting to multiple files later = routing function outputs to separate files. Trivial refactor because sections are already isolated.
## Must Generate
- Layer initialization calls (`linearLayerInit`, `reluLayerInit`, `softmaxLayerInit`, ...)
- Weight/bias tensor allocation with correct quantization config
- Conversion layer calls for ASYM quantization (`convertTensor`)
- Forward/backward pass wiring
- Training loop skeleton (TrainingApi)
- Optimizer setup (SgdApi)
- Loss function setup
- Memory management (reserveMemory/freeReservedMemory)
## Reference
`experiments/MnistExperiment.c` — hand-written example of the target output.
## Acceptance Criteria
- [ ] Spike: both Jinja2 and AST prototypes for comparison
- [ ] Decision documented with rationale
- [ ] Chosen approach implemented for Linear, ReLU, Softmax layers
- [ ] Generated C code compiles against the ODT C framework
- [ ] Generated training loop structurally matches MnistExperiment.c
- [ ] All three quantization types generate valid code (incl. ASYM conversion layers)
- [ ] Inference-only mode generates forward-pass-only code
- [ ] Tests verify generated code compiles (CI)
Contributor guide
Assessment
This issue has not been assessed yet.