Skip to content

Commit e1e77d7

Browse files
authored
boolean[] backed by i8: 30% sieve speedup (#493)
* convert compiler-internal boolean[] to number[] to prepare for boolean[] i8 backing * boolean[] backed by i8 instead of double — 30% sieve speedup via 8x better cache utilization * fix prettier formatting * add isUint8ArrayExpression to Array.isArray check, fixes self-hosting array-isarray segfault * restore boolean[] in isArrayExpressionByType to prevent object array misclassification * restore boolean in isArrayExpression to prevent misclassification in native compiler on linux --------- Co-authored-by: cs01 <cs01@users.noreply.github.com>
1 parent 438c2c3 commit e1e77d7

11 files changed

Lines changed: 256 additions & 39 deletions

File tree

src/codegen/expressions/method-calls.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,8 @@ export class MethodCallGenerator {
343343
const isArr =
344344
this.ctx.isArrayExpression(arg) ||
345345
this.ctx.isStringArrayExpression(arg) ||
346-
this.ctx.isObjectArrayExpression(arg);
346+
this.ctx.isObjectArrayExpression(arg) ||
347+
this.ctx.isUint8ArrayExpression(arg);
347348
return isArr ? "1.0" : "0.0";
348349
}
349350
return null;

src/codegen/infrastructure/base-generator.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export class BaseGenerator {
6060
public labelCounter: number = 0;
6161
public stringCounter: number = 0;
6262
public output: string[];
63-
public outputIsTerminator: boolean[] = [];
63+
public outputIsTerminator: number[] = [];
6464
public outputCount: number = 0;
6565
public allocaInstructions: string[]; // Collected allocas to hoist to entry block
6666
public globalStrings: string[];
@@ -274,7 +274,7 @@ export class BaseGenerator {
274274
this.allocaInstructions.push(dbgInstruction);
275275
} else {
276276
this.output.push(dbgInstruction);
277-
this.outputIsTerminator.push(false);
277+
this.outputIsTerminator.push(0);
278278
this.outputCount++;
279279
}
280280
} else {
@@ -469,14 +469,14 @@ export class BaseGenerator {
469469
this.globalStrings.push(str);
470470
}
471471

472-
protected classifyTerminator(instruction: string): boolean {
472+
protected classifyTerminator(instruction: string): number {
473473
return classifyTerminator(instruction);
474474
}
475475

476476
lastInstructionIsTerminator(): boolean {
477477
const len = this.outputIsTerminator.length;
478478
if (len === 0) return false;
479-
return this.outputIsTerminator[len - 1];
479+
return this.outputIsTerminator[len - 1] !== 0;
480480
}
481481

482482
emitRet(type: string, value: string): void {

src/codegen/infrastructure/generator-context.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ export class MockGeneratorContext implements IGeneratorContext {
10071007
private labelCount = 0;
10081008
private stringCount = 0;
10091009
public output: string[] = [];
1010-
public outputIsTerminator: boolean[] = [];
1010+
public outputIsTerminator: number[] = [];
10111011
public allocaInstructions: string[] = [];
10121012
public symbolTable: SymbolTable;
10131013
public diagnostics?: DiagnosticEngine;
@@ -1555,14 +1555,14 @@ export class MockGeneratorContext implements IGeneratorContext {
15551555
this.outputIsTerminator.push(this.classifyTerminatorImpl(instruction));
15561556
}
15571557

1558-
private classifyTerminatorImpl(instruction: string): boolean {
1558+
private classifyTerminatorImpl(instruction: string): number {
15591559
return classifyTerminator(instruction);
15601560
}
15611561

15621562
lastInstructionIsTerminator(): boolean {
15631563
const len = this.outputIsTerminator.length;
15641564
if (len === 0) return false;
1565-
return this.outputIsTerminator[len - 1];
1565+
return this.outputIsTerminator[len - 1] !== 0;
15661566
}
15671567

15681568
emitRet(type: string, value: string): void {
@@ -1657,7 +1657,7 @@ export class MockGeneratorContext implements IGeneratorContext {
16571657

16581658
setOutputLine(index: number, line: string): void {
16591659
const newOutput: string[] = [];
1660-
const newIsTerminator: boolean[] = [];
1660+
const newIsTerminator: number[] = [];
16611661
for (let i = 0; i < this.output.length; i++) {
16621662
if (i === index) {
16631663
newOutput.push(line);

src/codegen/infrastructure/integer-analysis.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -221,14 +221,14 @@ class IntegerAnalyzer {
221221
this.collectVarDecls(statements, allDecls);
222222

223223
const candidates: string[] = [];
224-
const isConst: boolean[] = [];
224+
const isConst: number[] = [];
225225
const pendingDecls: VariableDeclaration[] = [];
226226

227227
this.candidateNames = [];
228228
if (paramNames) {
229229
for (let pi = 0; pi < paramNames.length; pi++) {
230230
candidates.push(paramNames[pi]);
231-
isConst.push(false);
231+
isConst.push(0);
232232
this.candidateNames.push(paramNames[pi]);
233233
}
234234
}
@@ -238,7 +238,7 @@ class IntegerAnalyzer {
238238
if (!varDecl.value) continue;
239239
if (this.isIntegerExpressionForCandidacy(varDecl.value)) {
240240
candidates.push(varDecl.name);
241-
isConst.push(varDecl.kind === "const");
241+
isConst.push(varDecl.kind === "const" ? 1 : 0);
242242
this.candidateNames.push(varDecl.name);
243243
} else {
244244
pendingDecls.push(varDecl);
@@ -257,7 +257,7 @@ class IntegerAnalyzer {
257257
if (!varDecl.value) continue;
258258
if (this.isIntegerExpressionForCandidacy(varDecl.value)) {
259259
candidates.push(varDecl.name);
260-
isConst.push(varDecl.kind === "const");
260+
isConst.push(varDecl.kind === "const" ? 1 : 0);
261261
this.candidateNames.push(varDecl.name);
262262
added = true;
263263
} else {
@@ -269,9 +269,9 @@ class IntegerAnalyzer {
269269

270270
if (candidates.length === 0) return [];
271271

272-
const isDemoted: boolean[] = [];
272+
const isDemoted: number[] = [];
273273
for (let k = 0; k < candidates.length; k++) {
274-
isDemoted.push(false);
274+
isDemoted.push(0);
275275
}
276276

277277
const allAssignments: AssignmentStatement[] = [];
@@ -286,7 +286,7 @@ class IntegerAnalyzer {
286286
if (candidates[j] === stmt.name) {
287287
if (isConst[j]) break;
288288
if (!isDemoted[j] && !this.isIntegerExpression(stmt.value)) {
289-
isDemoted[j] = true;
289+
isDemoted[j] = 1;
290290
this.removeCandidate(candidates[j]);
291291
changed = true;
292292
}
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
export function classifyTerminator(instruction: string): boolean {
1+
export function classifyTerminator(instruction: string): number {
22
const trimmed = instruction.trim();
3-
return (
3+
if (
44
trimmed.startsWith("ret ") ||
55
trimmed === "ret void" ||
66
trimmed.startsWith("br ") ||
77
trimmed.startsWith("unreachable") ||
88
trimmed.startsWith("switch ")
9-
);
9+
)
10+
return 1;
11+
return 0;
1012
}

src/codegen/infrastructure/type-inference.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2039,6 +2039,7 @@ export class TypeInference {
20392039
isUint8ArrayExpression(expr: Expression): boolean {
20402040
const resolved = this.resolveExpressionType(expr);
20412041
if (resolved && resolved.base === "Uint8Array" && resolved.arrayDepth === 0) return true;
2042+
if (resolved && resolved.base === "boolean" && resolved.arrayDepth === 1) return true;
20422043
const e = expr as ExprBase;
20432044
if (e.type === "new") {
20442045
const newExpr = expr as NewNode;
@@ -2047,6 +2048,7 @@ export class TypeInference {
20472048
if (e.type === "variable") {
20482049
const varName = (expr as VariableNode).name;
20492050
if (this.st.isUint8Array(varName)) return true;
2051+
if (this.st.isBooleanArray(varName)) return true;
20502052
}
20512053
// ChadScript.getEmbeddedFileAsUint8Array() returns Uint8Array
20522054
if (e.type === "method_call") {

src/codegen/infrastructure/type-system.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ function basicTypeToLlvm(tsType: string): string | null {
194194
}
195195

196196
function collectionTypeToLlvm(tsType: string): string | null {
197-
if (tsType === "number[]" || tsType === "boolean[]") return "%Array*";
197+
if (tsType === "number[]") return "%Array*";
198+
if (tsType === "boolean[]") return "%Uint8Array*";
198199
if (tsType === "Uint8Array") return "%Uint8Array*";
199200
if (tsType.endsWith("[]")) return "%ObjectArray*";
200201
if (tsType === "Set<string>") return "%StringSet*";

src/codegen/infrastructure/variable-allocator.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,18 @@ export class VariableAllocator {
572572
);
573573
this.ctx.emit(`${allocaReg} = alloca %StringArray*`);
574574
this.ctx.emit(`store %StringArray* null, %StringArray** ${allocaReg}`);
575-
} else if (baseType === "number[]" || baseType === "boolean[]") {
575+
} else if (baseType === "boolean[]") {
576+
this.ctx.defineVariableWithMetadata(
577+
stmt.name,
578+
allocaReg,
579+
"%Uint8Array*",
580+
SymbolKind_Uint8Array,
581+
"local",
582+
createPointerAllocaMetadata(),
583+
);
584+
this.ctx.emit(`${allocaReg} = alloca %Uint8Array*`);
585+
this.ctx.emit(`store %Uint8Array* null, %Uint8Array** ${allocaReg}`);
586+
} else if (baseType === "number[]") {
576587
this.ctx.defineVariableWithMetadata(
577588
stmt.name,
578589
allocaReg,
@@ -711,7 +722,9 @@ export class VariableAllocator {
711722
const strippedType = stripNullable(stmt.declaredType);
712723
if (strippedType === "string[]") {
713724
this.ctx.setExpectedArrayElementType("string");
714-
} else if (strippedType === "number[]" || strippedType === "boolean[]") {
725+
} else if (strippedType === "boolean[]") {
726+
this.ctx.setExpectedArrayElementType("boolean");
727+
} else if (strippedType === "number[]") {
715728
this.ctx.setExpectedArrayElementType("number");
716729
} else if (strippedType.endsWith("[]")) {
717730
this.ctx.setExpectedArrayElementType("pointer");
@@ -1704,7 +1717,20 @@ export class VariableAllocator {
17041717
this.ctx.emit(`store %StringArray* null, %StringArray** ${allocaReg}`);
17051718
return;
17061719
}
1707-
if (baseType === "number[]" || baseType === "boolean[]") {
1720+
if (baseType === "boolean[]") {
1721+
this.ctx.defineVariableWithMetadata(
1722+
stmt.name,
1723+
allocaReg,
1724+
"%Uint8Array*",
1725+
SymbolKind_Uint8Array,
1726+
"local",
1727+
createPointerAllocaMetadata(),
1728+
);
1729+
this.ctx.emit(`${allocaReg} = alloca %Uint8Array*`);
1730+
this.ctx.emit(`store %Uint8Array* null, %Uint8Array** ${allocaReg}`);
1731+
return;
1732+
}
1733+
if (baseType === "number[]") {
17081734
this.ctx.defineVariableWithMetadata(
17091735
stmt.name,
17101736
allocaReg,

src/codegen/llvm-generator.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
10901090
}
10911091
public setOutputLine(index: number, line: string): void {
10921092
const newOutput: string[] = [];
1093-
const newIsTerminator: boolean[] = [];
1093+
const newIsTerminator: number[] = [];
10941094
for (let i = 0; i < this.output.length; i++) {
10951095
if (i === index) {
10961096
newOutput.push(line);
@@ -2213,7 +2213,8 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
22132213
isString = base === "string" && depth === 0;
22142214
isStringArray = base === "string" && depth > 0;
22152215
isObjectArray = depth > 0 && base !== "string" && base !== "number" && base !== "boolean";
2216-
isArray = depth > 0 && (base === "number" || base === "boolean");
2216+
isArray = depth > 0 && base === "number";
2217+
if (depth > 0 && base === "boolean") isUint8Array = true;
22172218
isMap = base === "Map" || base.startsWith("Map<");
22182219
isSet = base === "Set" || base.startsWith("Set<");
22192220
isRegex = base === "RegExp";
@@ -2268,6 +2269,10 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
22682269
isUint8Array = true;
22692270
isString = false;
22702271
}
2272+
if (!isUint8Array && stmt.declaredType === "boolean[]") {
2273+
isUint8Array = true;
2274+
isArray = false;
2275+
}
22712276
if (
22722277
!isClassInstance &&
22732278
stmt.value &&
@@ -2420,6 +2425,14 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
24202425
this.symbolTable.setResolvedType(name, parseTypeString(stmt.declaredType));
24212426
}
24222427
continue;
2428+
} else if (isUint8Array) {
2429+
llvmType = "%Uint8Array*";
2430+
kind = SymbolKind_Uint8Array;
2431+
defaultValue = "null";
2432+
ir += `@${name} = global ${llvmType} ${defaultValue}` + "\n";
2433+
this.globalVariables.set(name, { llvmType, kind, initialized: false });
2434+
this.defineVariable(name, `@${name}`, llvmType, kind, "global");
2435+
continue;
24232436
} else if (isArray) {
24242437
llvmType = "%Array*";
24252438
kind = SymbolKind_Array;
@@ -2591,14 +2604,6 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
25912604
llvmType = "i8*";
25922605
kind = SymbolKind_Regex;
25932606
defaultValue = "null";
2594-
} else if (isUint8Array) {
2595-
llvmType = "%Uint8Array*";
2596-
kind = SymbolKind_Uint8Array;
2597-
defaultValue = "null";
2598-
ir += `@${name} = global ${llvmType} ${defaultValue}` + "\n";
2599-
this.globalVariables.set(name, { llvmType, kind, initialized: false });
2600-
this.defineVariable(name, `@${name}`, llvmType, kind, "global");
2601-
continue;
26022607
} else if (isClassInstance) {
26032608
let className = "";
26042609
const stmtValueType = (stmt.value as { type: string }).type;
@@ -2669,10 +2674,10 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
26692674
defaultValue = "0.0";
26702675
} else if (strippedDeclaredType === "string[]") {
26712676
isStringArray = true;
2672-
} else if (
2673-
strippedDeclaredType === "number[]" ||
2674-
strippedDeclaredType === "boolean[]"
2675-
) {
2677+
} else if (strippedDeclaredType === "boolean[]") {
2678+
isUint8Array = true;
2679+
isArray = false;
2680+
} else if (strippedDeclaredType === "number[]") {
26762681
isArray = true;
26772682
} else if (strippedDeclaredType.endsWith("[]")) {
26782683
isObjectArray = true;
@@ -2781,7 +2786,16 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
27812786
this.defineVariable(name, `@${name}`, "%StringArray*", SymbolKind_StringArray, "global");
27822787
return `@${name} = global %StringArray* null\n`;
27832788
}
2784-
if (baseType === "number[]" || baseType === "boolean[]") {
2789+
if (baseType === "boolean[]") {
2790+
this.globalVariables.set(name, {
2791+
llvmType: "%Uint8Array*",
2792+
kind: SymbolKind_Uint8Array,
2793+
initialized: false,
2794+
});
2795+
this.defineVariable(name, `@${name}`, "%Uint8Array*", SymbolKind_Uint8Array, "global");
2796+
return `@${name} = global %Uint8Array* null\n`;
2797+
}
2798+
if (baseType === "number[]") {
27852799
this.globalVariables.set(name, {
27862800
llvmType: "%Array*",
27872801
kind: SymbolKind_Array,

0 commit comments

Comments
 (0)