evocompiler.com

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.

Install not published yet
The way in is one terminal command, and it is not ready. There is deliberately no browser download — macOS quarantines whatever a browser fetches and challenges it on launch, while a file fetched by a terminal command carries no quarantine flag at all. So the way in is a command where the problem does not arise. What exists today.
None of this is available to sign up for yet. The commands are not published, the agent connection is not stood up, accounts are not open yet, and projects do not exist yet. What exists today.
The opportunity

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.

Three overlapping circles with only a small shared centre filled in
General-purpose

It may only use what is safe for all of them at once.

A single circle, filled throughout
Tuned to you

One program, one target — the whole area is open.

How one is made

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.

Schematic a drawing of how the search narrows — not a record of a run
  1. 01 proposed Rewrites are proposed. Every one is a guess until it is checked. 12 marks
  2. 02 proved What survives its obligations. Anything that cannot be discharged is dropped. 7 marks
  3. 03 faster What also measured faster on the part you ship. Correct is not the same as faster. 4 marks
  4. 04 admitted What enters the library as a pass, with its preconditions attached. 2 marks
  5. 05 one compiler The admitted set, frozen with its tuned parameters. It runs offline. one
The marks are drawn, not counted. Twelve, seven, four, two: the shape of a narrowing, chosen to fit the picture. A real run's numbers live on that run's own page, beside the machine they were measured on. This is not that page.

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.

Your ordinary build Candidates, timed on the target
Each bar is one candidate's time on the target; shorter is faster. The dashed line is your ordinary build — the top candidate landed past it and lost, and when nothing beats it the baseline is what ships. Lengths are drawn to show the shape of the comparison. They are not a measurement, and this figure carries no scale, no axis and no numbers.
Tuning on an embedded target runs your program on the real part, many times. The board has to be disconnected from anything around it while that happens. What that means.
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.

Walk one example

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.

Representation
Source written for this page
/* 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;
}
IR illustrative, not the form evocc uses
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
}
Optimised illustrative in the same way, not compiler output
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
}
Target hand-written for this page, not compiler output
        @ 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
The record
Baseline what a general C compiler has to assume here
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
None of that is a fault. It is what being correct for every input costs.
Transformation peel and widen, one candidate
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
A transformation is not the rewrite. It is the rewrite plus the conditions under which the rewrite is allowed.
Obligations what had to be discharged, and what could not be
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
Anything that cannot be discharged is dropped. That is the whole gate.
A worked example written for this page, in the four shapes the same program passes through. The IR is a sketch of what such a form has to record rather than 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.
The boundary

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.

Choose a side of the boundary to read in detail
agent the one you already code with, over MCP, against a C program you point it at evolve the factory that assembles one compiler for one target search parameters searched against real runs rather than set by hand proving what a rewrite claims is discharged mechanically, or it is refused measurement on the part you actually ship, not on a model of it
One artifact crosses the boundary, in one direction A single arrow points downward from the design side to the build side, ending on a solid bar. Nothing is drawn travelling back up. the frozen compiler, once
01

It needs an account

Designing a compiler needs an account and a connection to the factory. Making one needs us; running one you already have does not. Where that stands today.

02

It needs your agent

A compiler is designed over MCP, by the agent you already code with, against a C program you point it at — real code rather than a description of it. What that involves.

03

It needs the real hardware

Tuning against a board means your code runs on that board repeatedly, in orders and timings you did not choose. Disconnect it from anything around it first — why, and the checklist.

04

It carries a credential

The agent connection is tied to your account, which is why the configuration lives behind sign-in rather than on a public page.

01

It runs offline

A compiler you have saved is yours. It runs on your own machine with no account, no lookup and no network, for as long as you like — that half is built, and a test walks the runner's import closure on every push so that nothing able to open a connection can get into it. You cannot get one today: accounts are not open and nothing has been published.

02

It is never slower

If the measured result does not beat your default compiler, the baseline is what ships, silently. The floor is the compiler you already had. Nothing enforces that floor today — the rule is written down as a single function every tier is required to share, and that function is not implemented yet.

03

It says when it found nothing

If no matching optimisation exists for your program it is compiled unchanged and you are told so. That is a normal outcome, not an error.

04

It is meant to be handed on

The design is that you give a colleague the one you built and it runs on their machine, offline — tuned for your code rather than theirs, so how much of the advantage survives depends on how close the two are. That path does not work yet: the part that matches a saved compiler against somebody else's source refuses every call it is given. If you were sent here holding one, start here.

A drawing of how the two halves relate. It carries no measurement, no timing and no quantity of any kind.

Start

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.