The demo below shows a small expression block. Click Step to let the CSE pass advance one step at a time, assigning value numbers and flagging repeated subexpressions. Click Run all to finish in one go, or Reset to start over with a fresh example.
<!-- CSE demo — value numbering approach -->
<p class="hint">A CSE pass assigns each unique computed value a <b>value number</b>. When a later instruction computes the same expression (same operator & operands), it gets the <i>same</i> value number — that instruction is redundant and can be replaced. Step through to watch it happen.</p>
<div id="code-block" class="code-block"></div>
<div id="status" class="status"></div>
<div class="btns">
<button id="btn-step" type="button">Step</button>
<button id="btn-run" type="button">Run all</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
/* styles for the CSE value-numbering demo */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .85rem; color: #444; margin: 0 0 .7rem; line-height: 1.5; }
.code-block { font: 14px ui-monospace, monospace; background: #f4f6f8;
border: 1px solid #cdd9e3; border-radius: 8px; padding: .7rem .9rem;
margin-bottom: .6rem; line-height: 1.7; }
.line { display: flex; align-items: baseline; gap: .5rem; padding: 1px 0; border-radius: 4px; transition: background .15s; }
.line.active { background: #fff3cd; }
.line.elim { background: #d4edda; }
.lnum { color: #999; font-size: .8em; min-width: 1.4em; text-align: right; user-select: none; }
.lcode { flex: 1; white-space: pre; }
.badge { font-size: .72em; padding: .1em .45em; border-radius: 4px; margin-left: .4rem; font-family: system-ui, sans-serif; font-weight: 600; }
.badge-new { background: #cfe2ff; color: #084298; }
.badge-reuse { background: #d1e7dd; color: #0a3622; }
.vnum { color: #6f42c1; font-weight: 700; }
.status { font-size: .95rem; font-weight: 600; min-height: 1.4em; margin-bottom: .5rem; }
.status.info { color: #1d3557; }
.status.done { color: #0a7d33; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .45; cursor: default; }
/* CSE value-numbering demo logic */
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
/* Sample instruction sequence: a*b appears at lines 0, 2; x*y at lines 5, 6; t1+e at line 4 reuses t1. */
const PROGRAM = [
{ lhs: 't1', op: '*', a: 'a', b: 'b' },
{ lhs: 't2', op: '+', a: 't1', b: 'c' },
{ lhs: 't3', op: '*', a: 'a', b: 'b' },
{ lhs: 't4', op: '-', a: 't3', b: 'd' },
{ lhs: 't5', op: '+', a: 't1', b: 'e' },
{ lhs: 't6', op: '*', a: 'x', b: 'y' },
{ lhs: 't7', op: '*', a: 'x', b: 'y' }
];
/* State: step index, value-number map (var -> vn), expression->vn map, per-line results. */
let step = 0;
let vnMap = {};
let exprMap = {};
let lineStates = [];
let nextVn = 1;
function exprKey(op, a, b) {
/* Canonical key for an expression using value numbers of operands. */
const va = vnMap[a] || a;
const vb = vnMap[b] || b;
return op + '(' + va + ',' + vb + ')';
}
function resetState() {
/* Reset all state to initial values. */
step = 0;
vnMap = {};
exprMap = {};
lineStates = PROGRAM.map(() => ({ vn: null, reuse: false, reuseOf: null }));
nextVn = 1;
}
function doStep() {
if (step >= PROGRAM.length) return;
const inst = PROGRAM[step];
const key = exprKey(inst.op, inst.a, inst.b);
if (exprMap[key] !== undefined) {
/* Expression already computed — reuse its value number. */
lineStates[step].reuse = true;
lineStates[step].reuseOf = exprMap[key];
lineStates[step].vn = lineStates[step].reuseOf;
} else {
/* First time we see this expression — assign a fresh value number. */
lineStates[step].vn = nextVn++;
exprMap[key] = lineStates[step].vn;
}
vnMap[inst.lhs] = lineStates[step].vn;
step++;
}
function lineText(inst) {
/* Format an instruction as readable source text. */
return inst.lhs + ' = ' + inst.a + ' ' + inst.op + ' ' + inst.b;
}
function render() {
const block = document.getElementById('code-block');
block.innerHTML = '';
PROGRAM.forEach((inst, i) => {
const div = document.createElement('div');
const state = lineStates[i];
let cls = 'line';
if (i === step && step < PROGRAM.length) cls += ' active';
else if (i < step && state.reuse) cls += ' elim';
div.className = cls;
const lnum = document.createElement('span');
lnum.className = 'lnum';
lnum.textContent = i + 1;
const code = document.createElement('span');
code.className = 'lcode';
code.textContent = lineText(inst);
div.appendChild(lnum);
div.appendChild(code);
if (i < step) {
const vn = document.createElement('span');
vn.className = 'vnum';
vn.textContent = ' #' + state.vn;
div.appendChild(vn);
const badge = document.createElement('span');
if (state.reuse) {
badge.className = 'badge badge-reuse';
badge.textContent = i18nFmt('reuse #%vn%', { vn: state.reuseOf });
} else {
badge.className = 'badge badge-new';
badge.textContent = 'new value';
}
div.appendChild(badge);
}
block.appendChild(div);
});
const status = document.getElementById('status');
if (step < PROGRAM.length) {
status.className = 'status info';
status.textContent = i18nFmt('Analyzing line %line% of %total%…', { line: step + 1, total: PROGRAM.length });
} else {
const saved = lineStates.filter(s => s.reuse).length;
status.className = 'status done';
status.textContent = i18nFmt('Done — %saved% redundant computation(s) eliminated.', { saved: saved });
}
document.getElementById('btn-step').disabled = step >= PROGRAM.length;
document.getElementById('btn-run').disabled = step >= PROGRAM.length;
}
document.getElementById('btn-step').addEventListener('click', () => { doStep(); render(); });
document.getElementById('btn-run').addEventListener('click', () => {
while (step < PROGRAM.length) doStep();
render();
});
document.getElementById('btn-reset').addEventListener('click', () => { resetState(); render(); });
resetState();
render();
<!-- CSE demo — value numbering approach -->
<p class="hint">A CSE pass assigns each unique computed value a <b>value number</b>. When a later instruction computes the same expression (same operator & operands), it gets the <i>same</i> value number — that instruction is redundant and can be replaced. Step through to watch it happen.</p>
<div id="code-block" class="code-block"></div>
<div id="status" class="status"></div>
<div class="btns">
<button id="btn-step" type="button">Step</button>
<button id="btn-run" type="button">Run all</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
/* styles for the CSE value-numbering demo */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .85rem; color: #444; margin: 0 0 .7rem; line-height: 1.5; }
.code-block { font: 14px ui-monospace, monospace; background: #f4f6f8;
border: 1px solid #cdd9e3; border-radius: 8px; padding: .7rem .9rem;
margin-bottom: .6rem; line-height: 1.7; }
.line { display: flex; align-items: baseline; gap: .5rem; padding: 1px 0; border-radius: 4px; transition: background .15s; }
.line.active { background: #fff3cd; }
.line.elim { background: #d4edda; }
.lnum { color: #999; font-size: .8em; min-width: 1.4em; text-align: right; user-select: none; }
.lcode { flex: 1; white-space: pre; }
.badge { font-size: .72em; padding: .1em .45em; border-radius: 4px; margin-left: .4rem; font-family: system-ui, sans-serif; font-weight: 600; }
.badge-new { background: #cfe2ff; color: #084298; }
.badge-reuse { background: #d1e7dd; color: #0a3622; }
.vnum { color: #6f42c1; font-weight: 700; }
.status { font-size: .95rem; font-weight: 600; min-height: 1.4em; margin-bottom: .5rem; }
.status.info { color: #1d3557; }
.status.done { color: #0a7d33; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button:disabled { opacity: .45; cursor: default; }
/* CSE value-numbering demo logic */
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
/* Sample instruction sequence: a*b appears at lines 0, 2; x*y at lines 5, 6; t1+e at line 4 reuses t1. */
const PROGRAM = [
{ lhs: 't1', op: '*', a: 'a', b: 'b' },
{ lhs: 't2', op: '+', a: 't1', b: 'c' },
{ lhs: 't3', op: '*', a: 'a', b: 'b' },
{ lhs: 't4', op: '-', a: 't3', b: 'd' },
{ lhs: 't5', op: '+', a: 't1', b: 'e' },
{ lhs: 't6', op: '*', a: 'x', b: 'y' },
{ lhs: 't7', op: '*', a: 'x', b: 'y' }
];
/* State: step index, value-number map (var -> vn), expression->vn map, per-line results. */
let step = 0;
let vnMap = {};
let exprMap = {};
let lineStates = [];
let nextVn = 1;
function exprKey(op, a, b) {
/* Canonical key for an expression using value numbers of operands. */
const va = vnMap[a] || a;
const vb = vnMap[b] || b;
return op + '(' + va + ',' + vb + ')';
}
function resetState() {
/* Reset all state to initial values. */
step = 0;
vnMap = {};
exprMap = {};
lineStates = PROGRAM.map(() => ({ vn: null, reuse: false, reuseOf: null }));
nextVn = 1;
}
function doStep() {
if (step >= PROGRAM.length) return;
const inst = PROGRAM[step];
const key = exprKey(inst.op, inst.a, inst.b);
if (exprMap[key] !== undefined) {
/* Expression already computed — reuse its value number. */
lineStates[step].reuse = true;
lineStates[step].reuseOf = exprMap[key];
lineStates[step].vn = lineStates[step].reuseOf;
} else {
/* First time we see this expression — assign a fresh value number. */
lineStates[step].vn = nextVn++;
exprMap[key] = lineStates[step].vn;
}
vnMap[inst.lhs] = lineStates[step].vn;
step++;
}
function lineText(inst) {
/* Format an instruction as readable source text. */
return inst.lhs + ' = ' + inst.a + ' ' + inst.op + ' ' + inst.b;
}
function render() {
const block = document.getElementById('code-block');
block.innerHTML = '';
PROGRAM.forEach((inst, i) => {
const div = document.createElement('div');
const state = lineStates[i];
let cls = 'line';
if (i === step && step < PROGRAM.length) cls += ' active';
else if (i < step && state.reuse) cls += ' elim';
div.className = cls;
const lnum = document.createElement('span');
lnum.className = 'lnum';
lnum.textContent = i + 1;
const code = document.createElement('span');
code.className = 'lcode';
code.textContent = lineText(inst);
div.appendChild(lnum);
div.appendChild(code);
if (i < step) {
const vn = document.createElement('span');
vn.className = 'vnum';
vn.textContent = ' #' + state.vn;
div.appendChild(vn);
const badge = document.createElement('span');
if (state.reuse) {
badge.className = 'badge badge-reuse';
badge.textContent = i18nFmt('reuse #%vn%', { vn: state.reuseOf });
} else {
badge.className = 'badge badge-new';
badge.textContent = 'new value';
}
div.appendChild(badge);
}
block.appendChild(div);
});
const status = document.getElementById('status');
if (step < PROGRAM.length) {
status.className = 'status info';
status.textContent = i18nFmt('Analyzing line %line% of %total%…', { line: step + 1, total: PROGRAM.length });
} else {
const saved = lineStates.filter(s => s.reuse).length;
status.className = 'status done';
status.textContent = i18nFmt('Done — %saved% redundant computation(s) eliminated.', { saved: saved });
}
document.getElementById('btn-step').disabled = step >= PROGRAM.length;
document.getElementById('btn-run').disabled = step >= PROGRAM.length;
}
document.getElementById('btn-step').addEventListener('click', () => { doStep(); render(); });
document.getElementById('btn-run').addEventListener('click', () => {
while (step < PROGRAM.length) doStep();
render();
});
document.getElementById('btn-reset').addEventListener('click', () => { resetState(); render(); });
resetState();
render();
Notice that CSE works by assigning each unique computed value a value number. Two subexpressions get the same value number when they produce the same result — that is the signal that the second occurrence is redundant and can be replaced. The key insight is that structural identity (same operator, same operands in the same order, same value numbers) is a sufficient condition for value identity, as long as no intervening assignment changes an operand.
Comments
Loading comments...