Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion ir/opt/ssa_opt.c
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,13 @@ int tcc_ir_ssa_opt_run(IRSSAOptCtx *ctx)
SSA_RUN("ssa:narrow", ssa_opt_narrow(ctx));
SSA_RUN("ssa:gvn", ssa_opt_gvn(ctx));
SSA_RUN("ssa:phi_simplify", ssa_opt_phi_simplify(ctx));
SSA_RUN("ssa:dead_loop", ssa_opt_dead_loop(ctx));
/* Dead-loop post-phi rewrite: collapse a side-effect-free counting loop
* whose result is a loop-invariant constant into a guarded constant
* (`(trip>0) ? body_const : init`). This is an -O2 optimization — GCC
* likewise keeps the empty loop at -O1 and only elides it at -O2 — so
* gate it to keep -O1 a lighter tier. */
SSA_RUN("ssa:dead_loop",
(tcc_state && tcc_state->optimize >= 2) ? ssa_opt_dead_loop(ctx) : 0);
SSA_RUN("ssa:dce", ssa_opt_dce(ctx));

/* target-specific generators (registered by backend) */
Expand Down
33 changes: 21 additions & 12 deletions libtcc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2271,7 +2271,12 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind)
break;
case TCC_OPTION_O:
s->optimize = atoi(optarg);
/* Enable all IR optimizations when -O1 or higher */
/* -O1: scalar cleanup, cheap ARM addressing-mode fusion, constant/range
* folding, loop rotation, and light inlining of explicitly-inline / tiny
* functions — the same class of work GCC does at -O1. The heavy tier
* (loop unrolling / IV strength reduction / LICM, MUL+ADD→MLA fusion,
* interprocedural const-prop, re-rolling, and full small-function
* inlining) is gated to -O2. */
if (s->optimize >= 1)
{
s->opt_dce = 1;
Expand All @@ -2295,28 +2300,32 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind)
* (tcc froze in parse_number on every integer literal).
* Without the fusion `*p++` lowers to an explicit
* LOAD + ADD whose result is written back correctly. */
s->opt_mla_fusion = 1; /* Fuse MUL+ADD into MLA */
/* fp-offset-cache disabled: miscompiles loops when combined with
iv-strength-red (e.g. SHA-1 sha_transform). Can still be
enabled manually with -ffp-offset-cache for debugging. */
s->opt_stack_addr_cse = 1; /* Hoist repeated stack address computations */
s->opt_licm = 1; /* Loop-invariant code motion */
s->opt_ipc = 1; /* Interprocedural constant propagation */
s->opt_strength_red = 1; /* Strength reduction for multiply */
s->opt_iv_strength_red = 1; /* IV strength reduction for array loops */
s->opt_loop_unroll = 1; /* Full-unroll small constant-trip-count loops */
s->opt_loop_rotation = 1; /* Rotate top-tested loops to bottom-tested */
s->opt_reroll = 1; /* Re-roll runs of identical macro-unrolled blocks */
s->opt_strength_red = 1; /* Strength reduction for multiply (peephole) */
s->opt_nonneg_fold = 1; /* Non-negative value branch folding */
s->opt_vrp = 1; /* Value range propagation branch folding */
s->opt_float_narrow = 1; /* Narrow double math to float when safe */
s->opt_jump_threading = 1; /* Jump threading optimization */
s->opt_loop_rotation = 1; /* Rotate top-tested loops to bottom-tested */
s->opt_inline_small = 1; /* Inline tiny static/inline functions (≤30 words) */
if (!s->opt_inline_limit)
s->opt_inline_limit = 30;
}
/* -O2: everything in -O1 plus the heavy tier — loop unrolling / IV
* strength reduction / LICM / re-rolling, MUL+ADD→MLA fusion,
* interprocedural constant propagation, and full small-function
* inlining. */
if (s->optimize >= 2)
{
s->opt_mla_fusion = 1; /* Fuse MUL+ADD into MLA */
/* fp-offset-cache disabled: miscompiles loops when combined with
iv-strength-red (e.g. SHA-1 sha_transform). Can still be
enabled manually with -ffp-offset-cache for debugging. */
s->opt_licm = 1; /* Loop-invariant code motion */
s->opt_ipc = 1; /* Interprocedural constant propagation */
s->opt_iv_strength_red = 1; /* IV strength reduction for array loops */
s->opt_loop_unroll = 1; /* Full-unroll small constant-trip-count loops */
s->opt_reroll = 1; /* Re-roll runs of identical macro-unrolled blocks */
s->opt_inline_functions = 1; /* Inline small static/inline functions (≤100 words) */
if (s->opt_inline_limit < 100)
s->opt_inline_limit = 100;
Expand Down
7 changes: 6 additions & 1 deletion metrics/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@
--
-- Apply with: sqlite3 metrics.db < metrics/schema.sql (safe to re-run).

PRAGMA journal_mode = WAL; -- Grafana reads never block the recorder's writes
-- NOT WAL: Grafana bind-mounts this file read-only (docker-compose.yml `:ro`),
-- and SQLite cannot open a WAL database read-only -- even a SELECT must write the
-- -wal/-shm sidecars, which fails with "attempt to write a readonly database".
-- Rollback-journal mode reads fine from read-only media; at a once-per-commit
-- write cadence the recorder (busy timeout 60s) and the dashboard never contend.
PRAGMA journal_mode = DELETE;
PRAGMA foreign_keys = ON;

-- One row per (commit, host). parent_sha = first parent, used by the
Expand Down
10 changes: 8 additions & 2 deletions tccgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -30526,7 +30526,10 @@ static void gen_function(Sym *sym)
* transformations and constant propagation have simplified loop bodies. */
if (tcc_state->opt_dce)
{
int dle_changes = tcc_ir_opt_dead_loop_elim(ir);
/* Dead-loop elimination collapses a side-effect-free loop into its final
* (constant) result. Gate to -O2 so -O1 keeps the loop, matching GCC's
* -O1 (which also only elides such loops at -O2). */
int dle_changes = (tcc_state->optimize >= 2) ? tcc_ir_opt_dead_loop_elim(ir) : 0;
if (dle_changes > 0)
{
tcc_ir_opt_value_tracking(ir);
Expand Down Expand Up @@ -30555,7 +30558,10 @@ static void gen_function(Sym *sym)
tcc_ir_opt_dead_var_store_elim(ir);
tcc_ir_opt_dse(ir);
}
int dle_changes = tcc_ir_opt_dead_loop_elim(ir);
/* Dead-loop elimination collapses a side-effect-free loop into its final
* (constant) result. Gate to -O2 so -O1 keeps the loop, matching GCC's
* -O1 (which also only elides such loops at -O2). */
int dle_changes = (tcc_state->optimize >= 2) ? tcc_ir_opt_dead_loop_elim(ir) : 0;
if (dle_changes > 0)
{
tcc_ir_opt_branch_folding(ir);
Expand Down
14 changes: 9 additions & 5 deletions tests/benchmarks/run_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import argparse
import os
import shutil
import socket
import subprocess
import sys
Expand Down Expand Up @@ -219,12 +220,15 @@ def build_compiler(compiler: str, ssh_host: str, opt_level: str = "1") -> Tuple[
if tcc_path:
print(f"Using TCC: {tcc_path}")
if check_compiler_changed(build_dir, compiler):
print(f"TCC compiler has been updated, forcing reconfiguration...")
print(f"TCC compiler has been updated, forcing a clean rebuild...")
force_reconfigure = True
# Remove CMake cache to force reconfiguration
cmake_cache = build_dir / "CMakeCache.txt"
if cmake_cache.exists():
cmake_cache.unlink()
# The compiler binary changed but the .c sources did not, so CMake's
# incremental build would happily reuse objects compiled by the OLD
# compiler (make only tracks source mtimes, not the compiler's). That
# silently benchmarks stale code. Wipe the whole build tree so every
# object — benchmark and SDK — is recompiled with the new compiler.
shutil.rmtree(build_dir, ignore_errors=True)
build_dir.mkdir(parents=True, exist_ok=True)

# Set environment with PICO_SDK_PATH
env = os.environ.copy()
Expand Down
Loading