Skip to content

Commit 4c538eb

Browse files
shai-almogclaude
andcommitted
js-port: CFG basic-block analysis (foundation for structured emission)
Add buildBasicBlocks() (instruction list -> basic blocks + successor edges) and isAcyclicForward() as the foundation for the structured control-flow (relooper) emitter that will replace the verbose while(true) switch(pc) state machine with if/else (and later while/for) for reducible CFGs -- keeping the generator threading model but emitting far more compact JS. Pure analysis: nothing consumes it for emission yet, so output is unchanged. The acyclic-if/else emitter (slice 1) lands on top next, gated behind a flag with switch(pc) as the irreducible fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6adfa4f commit 4c538eb

1 file changed

Lines changed: 124 additions & 0 deletions

File tree

vm/ByteCodeTranslator/src/com/codename1/tools/translator/JavascriptMethodGenerator.java

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3749,6 +3749,130 @@ private static boolean isPcSkippableNoOp(Instruction instruction) {
37493749
* label is purely decorative and a preceding ``pc = i; break;``
37503750
* may be omitted.
37513751
*/
3752+
/**
3753+
* A basic block: a maximal straight-line instruction range [start, end) with
3754+
* a single entry (start) and successor block indices. Foundation for the
3755+
* structured-control-flow (relooper) emitter that will replace the switch(pc)
3756+
* state machine with if/else/while for reducible CFGs (keeping the generator
3757+
* model). Pure analysis -- nothing consumes it for emission yet.
3758+
*/
3759+
static final class BasicBlock {
3760+
final int start;
3761+
int end;
3762+
final java.util.List<Integer> succs = new java.util.ArrayList<Integer>();
3763+
BasicBlock(int start) { this.start = start; }
3764+
}
3765+
3766+
/**
3767+
* Partition a method's instruction list into basic blocks and wire successor
3768+
* edges. Leaders: index 0, every branch/switch target, the instruction after a
3769+
* branch/return/throw, and try/catch range + handler boundaries.
3770+
*/
3771+
static java.util.List<BasicBlock> buildBasicBlocks(List<Instruction> instructions, Map<Label, Integer> labelToIndex) {
3772+
int n = instructions.size();
3773+
boolean[] leader = new boolean[n + 1];
3774+
if (n > 0) {
3775+
leader[0] = true;
3776+
}
3777+
for (int i = 0; i < n; i++) {
3778+
Instruction instr = instructions.get(i);
3779+
if (instr instanceof Jump) {
3780+
Integer t = labelToIndex.get(((Jump) instr).getLabel());
3781+
if (t != null && t < n) {
3782+
leader[t] = true;
3783+
}
3784+
if (i + 1 < n) {
3785+
leader[i + 1] = true;
3786+
}
3787+
} else if (instr instanceof SwitchInstruction) {
3788+
SwitchInstruction sw = (SwitchInstruction) instr;
3789+
if (sw.getDefaultLabel() != null) {
3790+
Integer d = labelToIndex.get(sw.getDefaultLabel());
3791+
if (d != null && d < n) { leader[d] = true; }
3792+
}
3793+
if (sw.getLabels() != null) {
3794+
for (Label l : sw.getLabels()) {
3795+
Integer d = l == null ? null : labelToIndex.get(l);
3796+
if (d != null && d < n) { leader[d] = true; }
3797+
}
3798+
}
3799+
if (i + 1 < n) { leader[i + 1] = true; }
3800+
} else if (instr instanceof TryCatch) {
3801+
TryCatch tc = (TryCatch) instr;
3802+
for (Label l : new Label[]{ tc.getStart(), tc.getEnd(), tc.getHandler() }) {
3803+
Integer d = l == null ? null : labelToIndex.get(l);
3804+
if (d != null && d < n) { leader[d] = true; }
3805+
}
3806+
} else if (isTerminatingInstruction(instr) && i + 1 < n) {
3807+
leader[i + 1] = true;
3808+
}
3809+
}
3810+
3811+
java.util.List<BasicBlock> blocks = new java.util.ArrayList<BasicBlock>();
3812+
java.util.Map<Integer, Integer> startToBlock = new java.util.HashMap<Integer, Integer>();
3813+
BasicBlock cur = null;
3814+
for (int i = 0; i < n; i++) {
3815+
if (leader[i] || cur == null) {
3816+
if (cur != null) { cur.end = i; }
3817+
cur = new BasicBlock(i);
3818+
startToBlock.put(i, blocks.size());
3819+
blocks.add(cur);
3820+
}
3821+
}
3822+
if (cur != null) { cur.end = n; }
3823+
3824+
for (BasicBlock b : blocks) {
3825+
Instruction last = b.end > b.start ? instructions.get(b.end - 1) : null;
3826+
boolean addFallThrough = true;
3827+
if (last instanceof Jump) {
3828+
Integer t = labelToIndex.get(((Jump) last).getLabel());
3829+
if (t != null && startToBlock.containsKey((int) t)) {
3830+
b.succs.add(startToBlock.get((int) t));
3831+
}
3832+
if (last.getOpcode() == Opcodes.GOTO) {
3833+
addFallThrough = false;
3834+
}
3835+
} else if (last instanceof SwitchInstruction) {
3836+
SwitchInstruction sw = (SwitchInstruction) last;
3837+
if (sw.getDefaultLabel() != null) {
3838+
Integer d = labelToIndex.get(sw.getDefaultLabel());
3839+
if (d != null && startToBlock.containsKey((int) d)) { b.succs.add(startToBlock.get((int) d)); }
3840+
}
3841+
if (sw.getLabels() != null) {
3842+
for (Label l : sw.getLabels()) {
3843+
Integer d = l == null ? null : labelToIndex.get(l);
3844+
if (d != null && startToBlock.containsKey((int) d)) { b.succs.add(startToBlock.get((int) d)); }
3845+
}
3846+
}
3847+
addFallThrough = false;
3848+
} else if (last != null && isTerminatingInstruction(last)) {
3849+
addFallThrough = false;
3850+
}
3851+
if (addFallThrough && startToBlock.containsKey(b.end)) {
3852+
b.succs.add(startToBlock.get(b.end));
3853+
}
3854+
}
3855+
return blocks;
3856+
}
3857+
3858+
/**
3859+
* Conservatively true when the block graph is acyclic in index order (no back
3860+
* edge: every successor block index is strictly greater than its source).
3861+
* Acyclic single-entry blocks are the slice the structured emitter handles
3862+
* first (if / if-else); methods with loops or any backward edge stay on
3863+
* switch(pc).
3864+
*/
3865+
static boolean isAcyclicForward(java.util.List<BasicBlock> blocks) {
3866+
for (int i = 0; i < blocks.size(); i++) {
3867+
for (int s : blocks.get(i).succs) {
3868+
if (s <= i) {
3869+
return false;
3870+
}
3871+
}
3872+
}
3873+
return true;
3874+
}
3875+
37523876
private static java.util.Set<Integer> computeJumpTargets(List<Instruction> instructions, Map<Label, Integer> labelToIndex,
37533877
boolean forceThrowingTargets) {
37543878
java.util.Set<Integer> targets = new java.util.HashSet<Integer>();

0 commit comments

Comments
 (0)