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
33 changes: 6 additions & 27 deletions src/codegen/expressions/access/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export interface IndexAccessGeneratorContext {
ensureDouble(value: string): string;
setUsesJson(value: boolean): void;
emitError(message: string, loc?: SourceLocation, suggestion?: string): never;
isSafeIndex(indexName: string, arrayName: string): boolean;
}

/**
Expand Down Expand Up @@ -182,19 +181,6 @@ export class IndexAccessGenerator {
return indexValue;
}

// Returns true when the given index access is a direct `arr[i]` pattern where
// the (i, arr) pair has been proven safe by loop analysis, so we can skip the
// runtime bounds check entirely.
private isProvenSafeAccess(expr: IndexAccessNode | IndexAccessAssignmentNode): boolean {
const obj = expr.object as ExprBase;
const idx = expr.index as ExprBase;
if (obj.type !== "variable") return false;
if (idx.type !== "variable") return false;
const arrName = (expr.object as VariableNode).name;
const idxName = (expr.index as VariableNode).name;
return this.ctx.isSafeIndex(idxName, arrName);
}

private emitBoundsCheck(arrayPtr: string, arrayType: string, index: string): void {
const lenPtr = this.ctx.nextTemp();
this.ctx.emit(
Expand Down Expand Up @@ -232,9 +218,7 @@ export class IndexAccessGenerator {
stringArrayPtr = cast;
}

if (!this.isProvenSafeAccess(expr)) {
this.emitBoundsCheck(stringArrayPtr, "%StringArray", index);
}
this.emitBoundsCheck(stringArrayPtr, "%StringArray", index);

const dataPtr = this.ctx.nextTemp();
this.ctx.emit(
Expand Down Expand Up @@ -265,9 +249,7 @@ export class IndexAccessGenerator {
arrayPtr = cast;
}

if (!this.isProvenSafeAccess(expr)) {
this.emitBoundsCheck(arrayPtr, "%Array", index);
}
this.emitBoundsCheck(arrayPtr, "%Array", index);

const dataPtr = this.ctx.nextTemp();
this.ctx.emit(`${dataPtr} = getelementptr inbounds %Array, %Array* ${arrayPtr}, i32 0, i32 0`);
Expand Down Expand Up @@ -334,10 +316,9 @@ export class IndexAccessGenerator {
const index = this.toI32Index(indexDouble);
const contiguousStride = this.getContiguousStride(expr);

const safeAccess = this.isProvenSafeAccess(expr);
const arrayType = this.ctx.getVariableType(arrayPtr);
if (arrayType === "%ObjectArray*") {
if (!safeAccess) this.emitBoundsCheck(arrayPtr, "%ObjectArray", index);
this.emitBoundsCheck(arrayPtr, "%ObjectArray", index);
if (contiguousStride > 0) {
return this.emitContiguousElementPtr(arrayPtr, "%ObjectArray", index, contiguousStride);
}
Expand All @@ -353,7 +334,7 @@ export class IndexAccessGenerator {
if (arrayType === "i8*") {
const arrayCast = this.ctx.nextTemp();
this.ctx.emit(`${arrayCast} = bitcast i8* ${arrayPtr} to %ObjectArray*`);
if (!safeAccess) this.emitBoundsCheck(arrayCast, "%ObjectArray", index);
this.emitBoundsCheck(arrayCast, "%ObjectArray", index);
if (contiguousStride > 0) {
return this.emitContiguousElementPtr(arrayCast, "%ObjectArray", index, contiguousStride);
}
Expand All @@ -366,7 +347,7 @@ export class IndexAccessGenerator {
return this.emitPointerArrayElementPtr(data, index);
}

if (!safeAccess) this.emitBoundsCheck(arrayPtr, "%ObjectArray", index);
this.emitBoundsCheck(arrayPtr, "%ObjectArray", index);
if (contiguousStride > 0) {
return this.emitContiguousElementPtr(arrayPtr, "%ObjectArray", index, contiguousStride);
}
Expand All @@ -385,9 +366,7 @@ export class IndexAccessGenerator {

const index = this.toI32Index(indexDouble);

if (!this.isProvenSafeAccess(expr)) {
this.emitBoundsCheck(arrayPtr, "%Uint8Array", index);
}
this.emitBoundsCheck(arrayPtr, "%Uint8Array", index);

const dataFieldPtr = this.ctx.nextTemp();
this.ctx.emit(
Expand Down
14 changes: 0 additions & 14 deletions src/codegen/infrastructure/generator-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -988,12 +988,6 @@ export interface IGeneratorContext {
currentVarDeclKey: string | null;
setCurrentVarDeclKey(key: string | null): void;
getCurrentVarDeclKey(): string | null;

// Bounds-check elimination (perf) — IMPORTANT: keep at END of interface.
pushSafeIndexScope(): void;
popSafeIndexScope(): void;
addSafeIndex(indexName: string, arrayName: string): void;
isSafeIndex(indexName: string, arrayName: string): boolean;
}

/**
Expand Down Expand Up @@ -2318,12 +2312,4 @@ export class MockGeneratorContext implements IGeneratorContext {
getCurrentVarDeclKey(): string | null {
return this.currentVarDeclKey;
}

// Bounds-check elimination stubs for MockGeneratorContext (no-ops).
pushSafeIndexScope(): void {}
popSafeIndexScope(): void {}
addSafeIndex(_indexName: string, _arrayName: string): void {}
isSafeIndex(_indexName: string, _arrayName: string): boolean {
return false;
}
}
41 changes: 0 additions & 41 deletions src/codegen/llvm-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,14 +330,6 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
public usesGC: number = 0;
public usesMathRandom: number = 0;

// Bounds-check elimination: parallel arrays tracking (indexVar, arrayVar) pairs
// that are proven safe within the currently active loop scope.
// Parallel arrays used instead of Map for self-hosting compatibility.
public safeIndexVars: string[] = [];
public safeIndexArrays: string[] = [];
// Per-loop-scope frame: each entry is how many pairs were added when that scope began.
public safeIndexFrames: number[] = [];

public emitError(message: string, loc?: SourceLocation, suggestion?: string): never {
this.diagnostics.error(message, loc, suggestion);
const output = this.diagnostics.formatDiagnostic(
Expand Down Expand Up @@ -1685,39 +1677,6 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
// LLVMGenerator-specific fields not in BaseGenerator
this.stringBuilderSlen.clear();
this.stringBuilderScap.clear();
this.safeIndexVars.length = 0;
this.safeIndexArrays.length = 0;
this.safeIndexFrames.length = 0;
}

// Push a new loop scope for bounds-check elimination tracking
pushSafeIndexScope(): void {
this.safeIndexFrames.push(this.safeIndexVars.length);
}

// Pop the most recent loop scope, removing any pairs added within it
popSafeIndexScope(): void {
if (this.safeIndexFrames.length === 0) return;
const frameStart = this.safeIndexFrames[this.safeIndexFrames.length - 1];
this.safeIndexFrames.pop();
this.safeIndexVars.length = frameStart;
this.safeIndexArrays.length = frameStart;
}

// Record that indexName indexing into arrayName is proven in-bounds
addSafeIndex(indexName: string, arrayName: string): void {
this.safeIndexVars.push(indexName);
this.safeIndexArrays.push(arrayName);
}

// Check whether indexName indexing arrayName is known safe in some enclosing loop scope
isSafeIndex(indexName: string, arrayName: string): boolean {
for (let i = this.safeIndexVars.length - 1; i >= 0; i--) {
if (this.safeIndexVars[i] === indexName && this.safeIndexArrays[i] === arrayName) {
return true;
}
}
return false;
}

getThisPointer(): string | null {
Expand Down
20 changes: 0 additions & 20 deletions src/codegen/statements/control-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import type { FieldInfo } from "../infrastructure/type-resolver/types.js";
import { stripOptional } from "../infrastructure/type-system.js";
import { setWantsI1 } from "../expressions/condition-generator.js";
import { tryOptimizeWhileLoopMap } from "./loop-idiom.js";
import { analyzeWhileSafety, analyzeForSafety } from "./loop-safety.js";

interface ExprBase {
type: string;
Expand Down Expand Up @@ -324,17 +323,7 @@ export class ControlFlowGenerator {
this.ctx.setCurrentLabel(bodyLabel);
this.loopContinueLabels.push(condLabel);
this.loopBreakLabels.push(endLabel);

// Bounds-check elimination: if the loop matches a safe pattern, register
// the (indexVar, arrayVar) pair so arr[i] inside body skips its bounds check.
const whileSafePat = analyzeWhileSafety(whileStmt);
this.ctx.pushSafeIndexScope();
if (whileSafePat) {
this.ctx.addSafeIndex(whileSafePat.indexName, whileSafePat.arrayName);
}

this.ctx.generateBlock(whileStmt.body, params);
this.ctx.popSafeIndexScope();
this.loopContinueLabels.pop();
this.loopBreakLabels.pop();
const bodyHasTerminator = this.ctx.lastInstructionIsTerminator();
Expand Down Expand Up @@ -486,16 +475,7 @@ export class ControlFlowGenerator {
this.ctx.setCurrentLabel(bodyLabel);
this.loopContinueLabels.push(updateLabel);
this.loopBreakLabels.push(endLabel);

// Bounds-check elimination: classic-for-loop safe index registration.
const forSafePat = analyzeForSafety(forStmt as ForStatement);
this.ctx.pushSafeIndexScope();
if (forSafePat) {
this.ctx.addSafeIndex(forSafePat.indexName, forSafePat.arrayName);
}

this.ctx.generateBlock(forStmt.body, params);
this.ctx.popSafeIndexScope();
this.loopContinueLabels.pop();
this.loopBreakLabels.pop();
const bodyHasTerminator3 = this.ctx.lastInstructionIsTerminator();
Expand Down
Loading
Loading