es-ude / es-ude/OnDeviceTraining

Implement ir2c — C code generation from annotated IR

Open
#62 0 comments 0 reactions 0 assignees View on GitHub
enhancement
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.