A compiler tuned to your project, not to the average of all programs.
General-purpose compilers optimise for every program at once — which means for none of them in particular. evolve works out which transformations are correct for your code on your target, tunes them against real measurements on that hardware, and freezes the result into a compiler you run offline, forever.
That is what evolve does, and it has done it on real hardware. None of it is open to the public yet — so read this page as a description of the system rather than of something you can run today.
Your program is not the average program.
A general compiler has one set of heuristics for every program anyone will ever write, on every machine they will ever run it on. It can only apply what is safe across all of them at once, and it cannot measure your code on your part, because it has never seen either.
What that leaves behind is the structure specific to your workload: the shapes your data actually takes, the operations your target actually has, the sizes your loops actually run. A compiler that knew those could commit where a general one must keep hedging.
It may only use what is safe for all of them at once.
One program, one target — the whole area is open.
Designed against your code, proved, measured, then frozen.
A compiler is designed by the agent you already work with, over MCP, against a C program you point it at. What comes out the far end is a binary.
- 01 proposed Rewrites are proposed. Every one is a guess until it is checked. 12 marks
- 02 proved What survives its obligations. Anything that cannot be discharged is dropped. 7 marks
- 03 faster What also measured faster on the part you ship. Correct is not the same as faster. 4 marks
- 04 admitted What enters the library as a pass, with its preconditions attached. 2 marks
- 05 one compiler The admitted set, frozen with its tuned parameters. It runs offline. one
Your agent, your program
You connect evolve to the agent you already code with, over MCP, and point it at a C program you already have — real code rather than a description of it. What comes back is a measurement against your own host's baseline compilers at their standard optimisation levels, not a model's opinion: no agent, no search and no model anywhere on that path.
- input
- one C program you point it at, and the target you build for
- driven by
- the coding agent you already use, over MCP
- today
- a narrow family of programs; anything outside it is refused rather than attempted
- needs
- an account
Correct before it is fast
A candidate arrives carrying the preconditions it assumes and the equivalence it claims. Those are discharged mechanically against what the function actually reads, writes and does, at the scope the rewrite applies to. Anything that cannot be discharged is refused, and the reason is kept — a rejection is a record rather than a shrug.
What ends up on your machine carries the matcher, not the prover.
- checks
- preconditions, and the equivalence claimed
- scope
- the region the rewrite applies to
- on failure
- refused, with the reason kept
On the part you actually ship
Candidates that survive are compiled and timed on the target you build for, interleaved against your ordinary build so both arms meet the same machine in the same state. Floating-point behaviour is held to the strict setting on both arms, and the difference against the baseline is checked against a stated tolerance — evidence about this run, never a proof.
If the measured result does not beat your default compiler, the baseline is what ships.
- measured on
- the target you build for
- compared against
- your ordinary build, interleaved
- if it is not faster
- the baseline ships
A compiler, not a service
What survives is pinned: a frozen, versioned composition together with the parameters that were measured for it. For a given version, identical input gives identical output, and it needs nothing from us to run — no account, no lookup, no network.
That also means it is a thing you can hand to someone else. It was tuned for one program, so how much of the advantage survives depends on how close theirs is to yours.
- form
- a versioned binary, with its tuned parameters
- determinism
- same input, same output, per version
- to run it
- nothing from us
The command your build calls
evocc is what a build system calls in place of gcc;
evolve manages your projects and the compilers on this machine. A build
whose compiler changed with nothing on screen saying so is the failure this product
exists to avoid — so invoking it, including under a compiler's name, resolves a
.evolve/ folder in your project that names which compiler to use, and it
stops if that folder is not there. The choice lives in your repository, where it can be
read and reviewed, rather than in your PATH.
The commands are not published yet. What exists today.
- the compiler command
- evocc
- the management command
- evolve
- what selects the compiler
- a .evolve/ folder in your project, not your PATH
Why measure instead of assume
On one worked example a hand-set parameter measured 3.13× where the searched one measured 9.77× on the same hardware. That is one example, measured on real hardware, not proof-backed, and not a promise about your code. It is the argument for measuring rather than assuming, and nothing more than that.
One small program, four times over.
The same small function as source, as an intermediate form, after the rewrites that survived, and as instructions for one part. Beside it, the record of what each rewrite had to hold and what could not be discharged. Nothing here was produced by a compiler, and every pane says so on its own header.
/* Add up n bytes. This says what, and nothing about how. */
#include <stddef.h>
#include <stdint.h>
uint32_t sum_bytes(const uint8_t *restrict p, size_t n)
{
uint32_t acc = 0;
for (size_t i = 0; i < n; ++i)
acc += p[i];
return acc;
}
region @sum_bytes {
facts
shapes p:[n] bytes n is known only at run time
aliasing p is not written here -- from restrict and const
numerics uint32 accumulator wraparound is defined
effects reads p[0..n), writes nothing
contract p addresses n readable bytes -- the caller's promise
reduce %acc = sum_i ( zext32( p[i] ) )
return %acc
hot the i-loop: n iterations, one byte each
}
region @sum_bytes.tgt {
peel i until p + i is 4-byte aligned
held: the peel establishes the alignment rather than
assuming it, and it runs at most 3 times
widen the aligned body loads 4 bytes at once
held: every byte it reads is inside p[0..n)
split %acc into four partial sums, added at the end
held: uint32 addition wraps, so regrouping returns
the same value for every input
guard tail of 0..3 bytes, added one at a time, kept
not admitted for this target:
read past the end to finish the last word
-- reads outside the object; the page after p may not exist
hoist the n == 0 test out of the loop
-- n == 0 is reachable, and the answer there is 0
}
@ hand-written for this page; not emitted by any compiler
@ target: armv7e-m, DSP extension present
@ on entry p is 4-byte aligned and r3 holds the number of
@ whole words; the peel above established both.
movs r5, #0 @ zero, for the byte differences
.Lword:
ldr r4, [r0], #4 @ four bytes at once
usada8 r2, r4, r5, r2 @ r2 += b0 + b1 + b2 + b3
subs r3, r3, #1
bne .Lword
@ tail: 0..3 bytes, still added one at a time
@ without the DSP extension this lowers to four widening adds
- loop order
- as written, one iteration at a time
- loads
- one byte per iteration
- alignment
- none assumed, because none is known
- trip count
- not known until the call happens
- rewrite
- peel, widen, split, guard the tail
- assumes
- p is readable across the whole of p[0..n)
- assumes
- uint32 addition wraps
- Held
- the value returned, for every n including 0
- Held
- nothing outside p[0..n) is read
- Held
- every load lies inside p[0..n)
- Held
- alignment is established by the peel, not assumed
- Held
- regrouping returns the same value, since uint32 addition wraps
- Held
- the 0..3 tail bytes are still added one at a time
- Held
- n == 0 is reachable, and the answer there is 0
- Not admitted
- read past the end to finish the last word
- Not admitted
- hoist the n == 0 test out of the loop
evocc's own, and no timing of any kind appears here. What each pane is
really showing is which rewrites were allowed and why the others were not.
Doing four bytes at once is not four times faster — what it is
worth is a question only measurement answers.
Designing needs us; using what you designed does not.
Everything expensive — the agent, the search, the proving, the runs on real hardware — happens on our side of a line. What crosses the line is a binary. After that your build is source, compiler, binary, and nothing else.
A drawing of how the two halves relate. It carries no measurement, no timing and no quantity of any kind.
Two ways in, and they open on different days.
An account is what lets you design a compiler against your own code. Installing gets you the commands to run one. Neither is open to the public yet, and the page each button leads to says exactly where that stands.