SAS/C Optimization

sc offers four optimization levels plus targeted switches for inlining, register allocation, and loop transformations. Source: Vol 2 Ch.12.

Optimization levels

Level Effect Recommended use
0 No optimization Debug builds; compiles fast, easy to step
1 Basic peephole (redundant moves, trivial simplifications) Quick dev rebuilds
2 Standard (common subexpression elimination, copy propagation, basic loop opts) Development builds
3 Aggressive (function inlining, advanced loop opts, jump threading) Release candidates
4 Maximum (vectorization, may alter floating-point precision) Release builds

The trade-off is consistent: higher level = slower compile, harder to debug, faster runtime. SAS/C documentation explicitly recommends:

  • Level 0 for development when stepping through code.
  • Level 2 for normal development builds.
  • Level 4 for the final release only.

Key optimization options

Option Effect
optimize=N Set optimization level (0–4)
inline Inline functions declared with __inline (default at optimize≥2)
noinline Disable inlining even at higher levels
inlinemanual Honor only explicit __inline directives (skip auto-inline)
inlinemax=N Cap auto-inlined function size at N statements (default 50)
inlinefplib Inline the FFP math library (large code size, fast execution)
noregargs Force stack-based calling even at high optimization
stackcheck Enable runtime stack overflow check (default on)
nostackcheck Disable stack check (slight speed gain)
autoinline Auto-inline small functions regardless of __inline
peephole Enable peephole optimizer (default)
nopeephole Disable peephole

What the optimizer does

Level 0 — no optimization

Each C statement becomes a fixed sequence of machine instructions. No reordering. No elimination of redundant work. Easy to debug — what you see in the source is what runs.

Level 1 — peephole

  • Redundant moves eliminated (move.l A0,A0 removed)
  • Constant folding (3+5 → 8)
  • Trivial strength reduction (x*2 → lsl #1)
  • Branch around single-instruction blocks flattened

Level 2 — standard

Level 1 plus: - Common subexpression elimination (CSE) - Copy propagation - Dead-code elimination (within basic blocks) - Strength reduction for induction variables - Loop-invariant code motion - Register allocation (linear scan)

At level 2, the compiled output is generally smaller and faster than level 1, with the caveat that debugging becomes harder — variables may be kept in registers and not visible in memory.

Level 3 — aggressive

Level 2 plus: - Function inlining (subject to inlinemax) - Cross-jump tail merging - Loop unrolling (small fixed-trip-count loops) - Jump threading - Global register allocation

Level 3 makes meaningful inlining decisions, so the source structure diverges noticeably from the machine code. Single-stepping at level 3 is mostly futile; set breakpoints instead.

Level 4 — maximum

Level 3 plus: - Vectorization of array loops (68040/060 with vector instructions) - More aggressive inlining - May re-order floating-point operations, altering rounding behavior

The FP precision warning is real: at level 4, a + b + c may be computed as a + (b + c) rather than (a + b) + c, which gives a different last-bit. This is acceptable for graphics but unacceptable for numerical analysis.

Interaction with debugging

The fundamental tension: optimizations make code faster by destroying the 1-to-1 mapping between source and machine code. At level 2+, a single source line may correspond to several non-contiguous machine instructions, or several source lines may share one basic block.

Optimization level Debug usability
0 Full: variables in memory, lines map 1:1 to instructions
1 Mostly full: peephole is local
2 Reasonable: line stepping works, but some variables may stay in registers
3 Limited: stepping jumps around, inlined functions are invisible
4 Not really debuggable: vectorization, reordering

For effective debugging, recompile the affected files at optimize=0 debug=line while keeping the rest of the program at the desired optimization level. cdbg can handle a mixed-mode build — set set trust-readonly-sections on if it complains about code section mismatches.

Profiling and measuring

sasc does not ship a profiler. To measure:

  1. Insert printf timing around code sections (cheapest, requires recompile).
  2. Use the timer.device via timer() from <dos/dos.h> or <clib/timer_protos.h>.
  3. Hand-instrument hot loops with the CyberGauge or equivalent cycle-counting tool.
  4. Run under cdbg with profiling hooks (slow; rarely useful in production).

The SAS/C compiler itself can emit a map file showing the size of each function — useful for finding unexpectedly large ones:

sc -M myprog.c                 # generate map output

When to optimize

The standard wisdom:

  1. Profile first. Don't optimize what isn't hot.
  2. Fix algorithmic issues before compiler options. A better algorithm beats a better optimizer.
  3. Optimize after correctness. Verify the unoptimized version is right first.
  4. Measure, don't assume. A "fast" optimization may not be measurably faster.

The AmigaOS-specific gotcha: many bottlenecks are not in your code but in the OS layer. A tight inner loop in C is often slower than the equivalent Intuition graphics primitive, or even a blit operation, because the OS can do in one blit what your loop does in 1000 iterations.

Compiler flags summary

A reasonable Makefile for an AmigaOS production build:

DEBUG_FLAGS = optimize=0 debug=line debug=local
RELEASE_FLAGS = optimize=4 structureequivalence
debug: CFLAGS = $(DEBUG_FLAGS)
release: CFLAGS = $(RELEASE_FLAGS)
all: myapp_debug myapp_release

myapp_debug: myapp.c
    sc link $(DEBUG_FLAGS) myapp.c to myapp_debug

myapp_release: myapp.c
    sc link $(RELEASE_FLAGS) myapp.c to myapp_release

See Also


Sources: SAS Institute Inc., "SAS/C Development System User's Guide, Volume 2" Ch.12, Version 6.50 (1993), pp. 339–388. Raw: raw/sasc/sasc-optimization-extract.md Updated: 2026-10-02