From f181faaa31d4e3bb1a37269718f5ebd400b19448 Mon Sep 17 00:00:00 2001 From: cs01 Date: Fri, 20 Mar 2026 23:16:06 -0700 Subject: [PATCH 1/6] convert compiler-internal boolean[] to number[] to prepare for boolean[] i8 backing --- src/codegen/infrastructure/base-generator.ts | 8 ++++---- src/codegen/infrastructure/generator-context.ts | 8 ++++---- src/codegen/infrastructure/integer-analysis.ts | 14 +++++++------- .../infrastructure/terminator-classifier.ts | 8 +++++--- src/codegen/llvm-generator.ts | 2 +- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/codegen/infrastructure/base-generator.ts b/src/codegen/infrastructure/base-generator.ts index 097ae1be..11cd113d 100644 --- a/src/codegen/infrastructure/base-generator.ts +++ b/src/codegen/infrastructure/base-generator.ts @@ -60,7 +60,7 @@ export class BaseGenerator { public labelCounter: number = 0; public stringCounter: number = 0; public output: string[]; - public outputIsTerminator: boolean[] = []; + public outputIsTerminator: number[] = []; public outputCount: number = 0; public allocaInstructions: string[]; // Collected allocas to hoist to entry block public globalStrings: string[]; @@ -274,7 +274,7 @@ export class BaseGenerator { this.allocaInstructions.push(dbgInstruction); } else { this.output.push(dbgInstruction); - this.outputIsTerminator.push(false); + this.outputIsTerminator.push(0); this.outputCount++; } } else { @@ -469,14 +469,14 @@ export class BaseGenerator { this.globalStrings.push(str); } - protected classifyTerminator(instruction: string): boolean { + protected classifyTerminator(instruction: string): number { return classifyTerminator(instruction); } lastInstructionIsTerminator(): boolean { const len = this.outputIsTerminator.length; if (len === 0) return false; - return this.outputIsTerminator[len - 1]; + return this.outputIsTerminator[len - 1] !== 0; } emitRet(type: string, value: string): void { diff --git a/src/codegen/infrastructure/generator-context.ts b/src/codegen/infrastructure/generator-context.ts index c37f893e..6c8054a1 100644 --- a/src/codegen/infrastructure/generator-context.ts +++ b/src/codegen/infrastructure/generator-context.ts @@ -1007,7 +1007,7 @@ export class MockGeneratorContext implements IGeneratorContext { private labelCount = 0; private stringCount = 0; public output: string[] = []; - public outputIsTerminator: boolean[] = []; + public outputIsTerminator: number[] = []; public allocaInstructions: string[] = []; public symbolTable: SymbolTable; public diagnostics?: DiagnosticEngine; @@ -1555,14 +1555,14 @@ export class MockGeneratorContext implements IGeneratorContext { this.outputIsTerminator.push(this.classifyTerminatorImpl(instruction)); } - private classifyTerminatorImpl(instruction: string): boolean { + private classifyTerminatorImpl(instruction: string): number { return classifyTerminator(instruction); } lastInstructionIsTerminator(): boolean { const len = this.outputIsTerminator.length; if (len === 0) return false; - return this.outputIsTerminator[len - 1]; + return this.outputIsTerminator[len - 1] !== 0; } emitRet(type: string, value: string): void { @@ -1657,7 +1657,7 @@ export class MockGeneratorContext implements IGeneratorContext { setOutputLine(index: number, line: string): void { const newOutput: string[] = []; - const newIsTerminator: boolean[] = []; + const newIsTerminator: number[] = []; for (let i = 0; i < this.output.length; i++) { if (i === index) { newOutput.push(line); diff --git a/src/codegen/infrastructure/integer-analysis.ts b/src/codegen/infrastructure/integer-analysis.ts index 8020d12b..60fdde52 100644 --- a/src/codegen/infrastructure/integer-analysis.ts +++ b/src/codegen/infrastructure/integer-analysis.ts @@ -221,14 +221,14 @@ class IntegerAnalyzer { this.collectVarDecls(statements, allDecls); const candidates: string[] = []; - const isConst: boolean[] = []; + const isConst: number[] = []; const pendingDecls: VariableDeclaration[] = []; this.candidateNames = []; if (paramNames) { for (let pi = 0; pi < paramNames.length; pi++) { candidates.push(paramNames[pi]); - isConst.push(false); + isConst.push(0); this.candidateNames.push(paramNames[pi]); } } @@ -238,7 +238,7 @@ class IntegerAnalyzer { if (!varDecl.value) continue; if (this.isIntegerExpressionForCandidacy(varDecl.value)) { candidates.push(varDecl.name); - isConst.push(varDecl.kind === "const"); + isConst.push(varDecl.kind === "const" ? 1 : 0); this.candidateNames.push(varDecl.name); } else { pendingDecls.push(varDecl); @@ -257,7 +257,7 @@ class IntegerAnalyzer { if (!varDecl.value) continue; if (this.isIntegerExpressionForCandidacy(varDecl.value)) { candidates.push(varDecl.name); - isConst.push(varDecl.kind === "const"); + isConst.push(varDecl.kind === "const" ? 1 : 0); this.candidateNames.push(varDecl.name); added = true; } else { @@ -269,9 +269,9 @@ class IntegerAnalyzer { if (candidates.length === 0) return []; - const isDemoted: boolean[] = []; + const isDemoted: number[] = []; for (let k = 0; k < candidates.length; k++) { - isDemoted.push(false); + isDemoted.push(0); } const allAssignments: AssignmentStatement[] = []; @@ -286,7 +286,7 @@ class IntegerAnalyzer { if (candidates[j] === stmt.name) { if (isConst[j]) break; if (!isDemoted[j] && !this.isIntegerExpression(stmt.value)) { - isDemoted[j] = true; + isDemoted[j] = 1; this.removeCandidate(candidates[j]); changed = true; } diff --git a/src/codegen/infrastructure/terminator-classifier.ts b/src/codegen/infrastructure/terminator-classifier.ts index ccff1f8c..28c0fc01 100644 --- a/src/codegen/infrastructure/terminator-classifier.ts +++ b/src/codegen/infrastructure/terminator-classifier.ts @@ -1,10 +1,12 @@ -export function classifyTerminator(instruction: string): boolean { +export function classifyTerminator(instruction: string): number { const trimmed = instruction.trim(); - return ( + if ( trimmed.startsWith("ret ") || trimmed === "ret void" || trimmed.startsWith("br ") || trimmed.startsWith("unreachable") || trimmed.startsWith("switch ") - ); + ) + return 1; + return 0; } diff --git a/src/codegen/llvm-generator.ts b/src/codegen/llvm-generator.ts index 11257072..8ba4e32c 100644 --- a/src/codegen/llvm-generator.ts +++ b/src/codegen/llvm-generator.ts @@ -1090,7 +1090,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { } public setOutputLine(index: number, line: string): void { const newOutput: string[] = []; - const newIsTerminator: boolean[] = []; + const newIsTerminator: number[] = []; for (let i = 0; i < this.output.length; i++) { if (i === index) { newOutput.push(line); From 1cce70a7a6fecddee3192985f76af4ec36e0cb96 Mon Sep 17 00:00:00 2001 From: cs01 Date: Fri, 20 Mar 2026 23:24:14 -0700 Subject: [PATCH 2/6] =?UTF-8?q?boolean[]=20backed=20by=20i8=20instead=20of?= =?UTF-8?q?=20double=20=E2=80=94=2030%=20sieve=20speedup=20via=208x=20bett?= =?UTF-8?q?er=20cache=20utilization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/codegen/infrastructure/type-inference.ts | 10 +- src/codegen/infrastructure/type-system.ts | 3 +- .../infrastructure/variable-allocator.ts | 32 +++++- src/codegen/llvm-generator.ts | 42 +++++--- .../types/collections/array/literal.ts | 73 ++++++++++++- .../types/collections/array/mutators.ts | 100 ++++++++++++++++++ 6 files changed, 237 insertions(+), 23 deletions(-) diff --git a/src/codegen/infrastructure/type-inference.ts b/src/codegen/infrastructure/type-inference.ts index 76b2e52b..cf9eadd7 100644 --- a/src/codegen/infrastructure/type-inference.ts +++ b/src/codegen/infrastructure/type-inference.ts @@ -1338,7 +1338,7 @@ export class TypeInference { if ( resolved && resolved.arrayDepth > 0 && - (resolved.base === "number" || resolved.base === "boolean") + resolved.base === "number" ) { return true; } @@ -1412,7 +1412,7 @@ export class TypeInference { } if (e.type === "call") { const rt = this.getCallReturnType(expr); - if (rt === "number[]" || rt === "boolean[]") return true; + if (rt === "number[]") return true; return false; } if (e.type === "member_access") { @@ -1431,7 +1431,7 @@ export class TypeInference { const className = this.st.getClassName((memberExpr.object as VariableNode).name); if (className) { const fieldType = this.ctx.classGenGetFieldType(className, memberExpr.property); - if (fieldType === "number[]" || fieldType === "boolean[]") { + if (fieldType === "number[]") { return true; } } @@ -1440,7 +1440,7 @@ export class TypeInference { const className = this.ctx.getCurrentClassName(); if (className) { const fieldType = this.ctx.classGenGetFieldType(className, memberExpr.property); - if (fieldType === "number[]" || fieldType === "boolean[]") { + if (fieldType === "number[]") { return true; } } @@ -2039,6 +2039,7 @@ export class TypeInference { isUint8ArrayExpression(expr: Expression): boolean { const resolved = this.resolveExpressionType(expr); if (resolved && resolved.base === "Uint8Array" && resolved.arrayDepth === 0) return true; + if (resolved && resolved.base === "boolean" && resolved.arrayDepth === 1) return true; const e = expr as ExprBase; if (e.type === "new") { const newExpr = expr as NewNode; @@ -2047,6 +2048,7 @@ export class TypeInference { if (e.type === "variable") { const varName = (expr as VariableNode).name; if (this.st.isUint8Array(varName)) return true; + if (this.st.isBooleanArray(varName)) return true; } // ChadScript.getEmbeddedFileAsUint8Array() returns Uint8Array if (e.type === "method_call") { diff --git a/src/codegen/infrastructure/type-system.ts b/src/codegen/infrastructure/type-system.ts index f989de00..561de041 100644 --- a/src/codegen/infrastructure/type-system.ts +++ b/src/codegen/infrastructure/type-system.ts @@ -194,7 +194,8 @@ function basicTypeToLlvm(tsType: string): string | null { } function collectionTypeToLlvm(tsType: string): string | null { - if (tsType === "number[]" || tsType === "boolean[]") return "%Array*"; + if (tsType === "number[]") return "%Array*"; + if (tsType === "boolean[]") return "%Uint8Array*"; if (tsType === "Uint8Array") return "%Uint8Array*"; if (tsType.endsWith("[]")) return "%ObjectArray*"; if (tsType === "Set") return "%StringSet*"; diff --git a/src/codegen/infrastructure/variable-allocator.ts b/src/codegen/infrastructure/variable-allocator.ts index 5bbfaf15..4c867fd4 100644 --- a/src/codegen/infrastructure/variable-allocator.ts +++ b/src/codegen/infrastructure/variable-allocator.ts @@ -572,7 +572,18 @@ export class VariableAllocator { ); this.ctx.emit(`${allocaReg} = alloca %StringArray*`); this.ctx.emit(`store %StringArray* null, %StringArray** ${allocaReg}`); - } else if (baseType === "number[]" || baseType === "boolean[]") { + } else if (baseType === "boolean[]") { + this.ctx.defineVariableWithMetadata( + stmt.name, + allocaReg, + "%Uint8Array*", + SymbolKind_Uint8Array, + "local", + createPointerAllocaMetadata(), + ); + this.ctx.emit(`${allocaReg} = alloca %Uint8Array*`); + this.ctx.emit(`store %Uint8Array* null, %Uint8Array** ${allocaReg}`); + } else if (baseType === "number[]") { this.ctx.defineVariableWithMetadata( stmt.name, allocaReg, @@ -711,7 +722,9 @@ export class VariableAllocator { const strippedType = stripNullable(stmt.declaredType); if (strippedType === "string[]") { this.ctx.setExpectedArrayElementType("string"); - } else if (strippedType === "number[]" || strippedType === "boolean[]") { + } else if (strippedType === "boolean[]") { + this.ctx.setExpectedArrayElementType("boolean"); + } else if (strippedType === "number[]") { this.ctx.setExpectedArrayElementType("number"); } else if (strippedType.endsWith("[]")) { this.ctx.setExpectedArrayElementType("pointer"); @@ -1704,7 +1717,20 @@ export class VariableAllocator { this.ctx.emit(`store %StringArray* null, %StringArray** ${allocaReg}`); return; } - if (baseType === "number[]" || baseType === "boolean[]") { + if (baseType === "boolean[]") { + this.ctx.defineVariableWithMetadata( + stmt.name, + allocaReg, + "%Uint8Array*", + SymbolKind_Uint8Array, + "local", + createPointerAllocaMetadata(), + ); + this.ctx.emit(`${allocaReg} = alloca %Uint8Array*`); + this.ctx.emit(`store %Uint8Array* null, %Uint8Array** ${allocaReg}`); + return; + } + if (baseType === "number[]") { this.ctx.defineVariableWithMetadata( stmt.name, allocaReg, diff --git a/src/codegen/llvm-generator.ts b/src/codegen/llvm-generator.ts index 8ba4e32c..59804cbb 100644 --- a/src/codegen/llvm-generator.ts +++ b/src/codegen/llvm-generator.ts @@ -2213,7 +2213,8 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { isString = base === "string" && depth === 0; isStringArray = base === "string" && depth > 0; isObjectArray = depth > 0 && base !== "string" && base !== "number" && base !== "boolean"; - isArray = depth > 0 && (base === "number" || base === "boolean"); + isArray = depth > 0 && base === "number"; + if (depth > 0 && base === "boolean") isUint8Array = true; isMap = base === "Map" || base.startsWith("Map<"); isSet = base === "Set" || base.startsWith("Set<"); isRegex = base === "RegExp"; @@ -2268,6 +2269,10 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { isUint8Array = true; isString = false; } + if (!isUint8Array && stmt.declaredType === "boolean[]") { + isUint8Array = true; + isArray = false; + } if ( !isClassInstance && stmt.value && @@ -2420,6 +2425,14 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { this.symbolTable.setResolvedType(name, parseTypeString(stmt.declaredType)); } continue; + } else if (isUint8Array) { + llvmType = "%Uint8Array*"; + kind = SymbolKind_Uint8Array; + defaultValue = "null"; + ir += `@${name} = global ${llvmType} ${defaultValue}` + "\n"; + this.globalVariables.set(name, { llvmType, kind, initialized: false }); + this.defineVariable(name, `@${name}`, llvmType, kind, "global"); + continue; } else if (isArray) { llvmType = "%Array*"; kind = SymbolKind_Array; @@ -2591,14 +2604,6 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { llvmType = "i8*"; kind = SymbolKind_Regex; defaultValue = "null"; - } else if (isUint8Array) { - llvmType = "%Uint8Array*"; - kind = SymbolKind_Uint8Array; - defaultValue = "null"; - ir += `@${name} = global ${llvmType} ${defaultValue}` + "\n"; - this.globalVariables.set(name, { llvmType, kind, initialized: false }); - this.defineVariable(name, `@${name}`, llvmType, kind, "global"); - continue; } else if (isClassInstance) { let className = ""; const stmtValueType = (stmt.value as { type: string }).type; @@ -2669,10 +2674,10 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { defaultValue = "0.0"; } else if (strippedDeclaredType === "string[]") { isStringArray = true; - } else if ( - strippedDeclaredType === "number[]" || - strippedDeclaredType === "boolean[]" - ) { + } else if (strippedDeclaredType === "boolean[]") { + isUint8Array = true; + isArray = false; + } else if (strippedDeclaredType === "number[]") { isArray = true; } else if (strippedDeclaredType.endsWith("[]")) { isObjectArray = true; @@ -2781,7 +2786,16 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext { this.defineVariable(name, `@${name}`, "%StringArray*", SymbolKind_StringArray, "global"); return `@${name} = global %StringArray* null\n`; } - if (baseType === "number[]" || baseType === "boolean[]") { + if (baseType === "boolean[]") { + this.globalVariables.set(name, { + llvmType: "%Uint8Array*", + kind: SymbolKind_Uint8Array, + initialized: false, + }); + this.defineVariable(name, `@${name}`, "%Uint8Array*", SymbolKind_Uint8Array, "global"); + return `@${name} = global %Uint8Array* null\n`; + } + if (baseType === "number[]") { this.globalVariables.set(name, { llvmType: "%Array*", kind: SymbolKind_Array, diff --git a/src/codegen/types/collections/array/literal.ts b/src/codegen/types/collections/array/literal.ts index b4274023..dee8c492 100644 --- a/src/codegen/types/collections/array/literal.ts +++ b/src/codegen/types/collections/array/literal.ts @@ -93,7 +93,78 @@ export function generateArrayLiteral( } } - if (isStringArray) { + let isBooleanArray = false; + if (length > 0 && !isStringArray && !isPointerArray) { + let allBooleans = true; + for (let i = 0; i < arrExpr.elements.length; i++) { + const el = arrExpr.elements[i] as { type: string }; + if (el.type !== "boolean") { + allBooleans = false; + break; + } + } + isBooleanArray = allBooleans; + } + if (length === 0 && gen.getExpectedArrayElementType() === "boolean") { + isBooleanArray = true; + } + + if (isBooleanArray) { + const sizePtr = gen.nextTemp(); + gen.emit(`${sizePtr} = getelementptr %Uint8Array, %Uint8Array* null, i32 1`); + const structSize = gen.nextTemp(); + gen.emit(`${structSize} = ptrtoint %Uint8Array* ${sizePtr} to i64`); + const arrayMem = gen.emitCall("i8*", "@GC_malloc", `i64 ${structSize}`); + const arrayPtr = gen.emitBitcast(arrayMem, "i8*", "%Uint8Array*"); + + const dataCount = length === 0 ? 1 : length; + const dataSize = gen.nextTemp(); + gen.emit(`${dataSize} = mul i64 ${dataCount}, 1`); + const dataMem = gen.emitCall("i8*", "@cs_arena_alloc", `i64 ${dataSize}`); + + for (let i = 0; i < arrExpr.elements.length; i++) { + const elemValue = + i === 0 && firstElemValue + ? firstElemValue + : gen.generateExpression(arrExpr.elements[i], params); + const elemType = gen.getVariableType(elemValue); + let i8Val: string; + if (elemType === "i1") { + i8Val = gen.nextTemp(); + gen.emit(`${i8Val} = zext i1 ${elemValue} to i8`); + } else { + const dblElem = gen.ensureDouble(elemValue); + const rawI8 = gen.nextTemp(); + gen.emit(`${rawI8} = fptosi double ${dblElem} to i8`); + i8Val = gen.nextTemp(); + gen.emit(`${i8Val} = and i8 ${rawI8}, 1`); + } + const elemPtr = gen.nextTemp(); + gen.emit(`${elemPtr} = getelementptr inbounds i8, i8* ${dataMem}, i32 ${i}`); + gen.emitStore("i8", i8Val, elemPtr); + } + + const dataPtrField = gen.nextTemp(); + gen.emit( + `${dataPtrField} = getelementptr inbounds %Uint8Array, %Uint8Array* ${arrayPtr}, i32 0, i32 0`, + ); + gen.emitStore("i8*", dataMem, dataPtrField); + + const lenField = gen.nextTemp(); + gen.emit( + `${lenField} = getelementptr inbounds %Uint8Array, %Uint8Array* ${arrayPtr}, i32 0, i32 1`, + ); + gen.emitStore("i32", `${length}`, lenField); + + const capField = gen.nextTemp(); + gen.emit( + `${capField} = getelementptr inbounds %Uint8Array, %Uint8Array* ${arrayPtr}, i32 0, i32 2`, + ); + gen.emitStore("i32", `${length}`, capField); + + gen.setVariableType(arrayPtr, "%Uint8Array*"); + return arrayPtr; + } else if (isStringArray) { // Generate string array - allocate on HEAP, not stack // Compute sizeof(%StringArray) dynamically for portability const sizePtr = gen.nextTemp(); diff --git a/src/codegen/types/collections/array/mutators.ts b/src/codegen/types/collections/array/mutators.ts index 6abd6d60..f3f8ca4b 100644 --- a/src/codegen/types/collections/array/mutators.ts +++ b/src/codegen/types/collections/array/mutators.ts @@ -42,6 +42,21 @@ export function generateArrayPush( else if (ptrType === "%ObjectArray*" || ptrType === "%ObjectArray") isObjectArray = true; } + let isUint8Array = false; + if (exprObjBase.type === "variable") { + const varName = (expr.object as VariableNode).name; + const varType = gen.getVariableType(varName); + if (varType === "%Uint8Array*" || varType === "%Uint8Array") isUint8Array = true; + } + if (!isUint8Array) { + const ptrType = gen.getVariableType(arrayPtr); + if (ptrType === "%Uint8Array*" || ptrType === "%Uint8Array") isUint8Array = true; + } + + if (isUint8Array) { + return generateUint8ArrayPush(gen, arrayPtr, value); + } + if (isStringArray) { return generateStringArrayPush(gen, arrayPtr, value); } @@ -417,6 +432,91 @@ function generateIntArrayPush(gen: IGeneratorContext, arrayPtr: string, value: s return newLenDouble; } +function generateUint8ArrayPush(gen: IGeneratorContext, arrayPtr: string, value: string): string { + const lenPtr = gen.nextTemp(); + gen.emit( + `${lenPtr} = getelementptr inbounds %Uint8Array, %Uint8Array* ${arrayPtr}, i32 0, i32 1`, + ); + const currentLen = gen.nextTemp(); + gen.emit(`${currentLen} = load i32, i32* ${lenPtr}`); + + const capPtr = gen.nextTemp(); + gen.emit( + `${capPtr} = getelementptr inbounds %Uint8Array, %Uint8Array* ${arrayPtr}, i32 0, i32 2`, + ); + const currentCap = gen.nextTemp(); + gen.emit(`${currentCap} = load i32, i32* ${capPtr}`); + + const needResize = gen.emitIcmp("eq", "i32", currentLen, currentCap); + + const resizeLabel = gen.nextLabel("resize"); + const continueLabel = gen.nextLabel("continue"); + + gen.emitBrCond(needResize, resizeLabel, continueLabel); + + gen.emitLabel(resizeLabel); + const isZero = gen.emitIcmp("eq", "i32", currentCap, "0"); + const doubled = gen.nextTemp(); + gen.emit(`${doubled} = mul i32 ${currentCap}, 2`); + const newCap = gen.nextTemp(); + gen.emit(`${newCap} = select i1 ${isZero}, i32 2, i32 ${doubled}`); + + const newCapI64 = gen.nextTemp(); + gen.emit(`${newCapI64} = zext i32 ${newCap} to i64`); + const newMem = gen.emitCall("i8*", "@cs_arena_alloc", `i64 ${newCapI64}`); + + const dataPtrField = gen.nextTemp(); + gen.emit( + `${dataPtrField} = getelementptr inbounds %Uint8Array, %Uint8Array* ${arrayPtr}, i32 0, i32 0`, + ); + const oldDataPtr = gen.emitLoad("i8*", dataPtrField); + + const currentLenI64 = gen.nextTemp(); + gen.emit(`${currentLenI64} = zext i32 ${currentLen} to i64`); + gen.emit( + `call void @llvm.memcpy.p0i8.p0i8.i64(i8* ${newMem}, i8* ${oldDataPtr}, i64 ${currentLenI64}, i1 false)`, + ); + + gen.emitStore("i8*", newMem, dataPtrField); + gen.emitStore("i32", newCap, capPtr); + + gen.emitBr(continueLabel); + + gen.emitLabel(continueLabel); + + const dataPtrField2 = gen.nextTemp(); + gen.emit( + `${dataPtrField2} = getelementptr inbounds %Uint8Array, %Uint8Array* ${arrayPtr}, i32 0, i32 0`, + ); + const dataPtr = gen.emitLoad("i8*", dataPtrField2); + + const valueType = gen.getVariableType(value); + let i8Val: string; + if (valueType === "i1") { + i8Val = gen.nextTemp(); + gen.emit(`${i8Val} = zext i1 ${value} to i8`); + } else { + const dblValue = gen.ensureDouble(value); + const rawI8 = gen.nextTemp(); + gen.emit(`${rawI8} = fptosi double ${dblValue} to i8`); + i8Val = gen.nextTemp(); + gen.emit(`${i8Val} = and i8 ${rawI8}, 1`); + } + + const elemPtr = gen.nextTemp(); + gen.emit(`${elemPtr} = getelementptr inbounds i8, i8* ${dataPtr}, i32 ${currentLen}`); + gen.emitStore("i8", i8Val, elemPtr); + + const newLen = gen.nextTemp(); + gen.emit(`${newLen} = add i32 ${currentLen}, 1`); + gen.emitStore("i32", newLen, lenPtr); + + const newLenDouble = gen.nextTemp(); + gen.emit(`${newLenDouble} = sitofp i32 ${newLen} to double`); + gen.setVariableType(newLenDouble, "double"); + return newLenDouble; +} + function generateStringArrayPush(gen: IGeneratorContext, arrayPtr: string, value: string): string { // Push to %StringArray (string array) From 06517204aed40af5bea5a22f80d2548ae3b4b6a9 Mon Sep 17 00:00:00 2001 From: cs01 Date: Fri, 20 Mar 2026 23:27:07 -0700 Subject: [PATCH 3/6] fix prettier formatting --- src/codegen/infrastructure/type-inference.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/codegen/infrastructure/type-inference.ts b/src/codegen/infrastructure/type-inference.ts index cf9eadd7..eec512e3 100644 --- a/src/codegen/infrastructure/type-inference.ts +++ b/src/codegen/infrastructure/type-inference.ts @@ -1335,11 +1335,7 @@ export class TypeInference { } if (e.type === "variable" || e.type === "index_access") { const resolved = this.resolveExpressionType(expr); - if ( - resolved && - resolved.arrayDepth > 0 && - resolved.base === "number" - ) { + if (resolved && resolved.arrayDepth > 0 && resolved.base === "number") { return true; } if (e.type === "index_access") return false; From 89f6e06c03ca0660582d062f132a5dd4668b2e63 Mon Sep 17 00:00:00 2001 From: cs01 Date: Fri, 20 Mar 2026 23:46:59 -0700 Subject: [PATCH 4/6] add isUint8ArrayExpression to Array.isArray check, fixes self-hosting array-isarray segfault --- src/codegen/expressions/method-calls.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/codegen/expressions/method-calls.ts b/src/codegen/expressions/method-calls.ts index 3a21a59f..b294ab88 100644 --- a/src/codegen/expressions/method-calls.ts +++ b/src/codegen/expressions/method-calls.ts @@ -343,7 +343,8 @@ export class MethodCallGenerator { const isArr = this.ctx.isArrayExpression(arg) || this.ctx.isStringArrayExpression(arg) || - this.ctx.isObjectArrayExpression(arg); + this.ctx.isObjectArrayExpression(arg) || + this.ctx.isUint8ArrayExpression(arg); return isArr ? "1.0" : "0.0"; } return null; From 27e0c8ec76c5ab9d3bac2eb4827f81896f6424bc Mon Sep 17 00:00:00 2001 From: cs01 Date: Sat, 21 Mar 2026 00:02:54 -0700 Subject: [PATCH 5/6] restore boolean[] in isArrayExpressionByType to prevent object array misclassification --- src/codegen/infrastructure/type-inference.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/codegen/infrastructure/type-inference.ts b/src/codegen/infrastructure/type-inference.ts index eec512e3..69818197 100644 --- a/src/codegen/infrastructure/type-inference.ts +++ b/src/codegen/infrastructure/type-inference.ts @@ -1408,7 +1408,7 @@ export class TypeInference { } if (e.type === "call") { const rt = this.getCallReturnType(expr); - if (rt === "number[]") return true; + if (rt === "number[]" || rt === "boolean[]") return true; return false; } if (e.type === "member_access") { @@ -1427,7 +1427,7 @@ export class TypeInference { const className = this.st.getClassName((memberExpr.object as VariableNode).name); if (className) { const fieldType = this.ctx.classGenGetFieldType(className, memberExpr.property); - if (fieldType === "number[]") { + if (fieldType === "number[]" || fieldType === "boolean[]") { return true; } } @@ -1436,7 +1436,7 @@ export class TypeInference { const className = this.ctx.getCurrentClassName(); if (className) { const fieldType = this.ctx.classGenGetFieldType(className, memberExpr.property); - if (fieldType === "number[]") { + if (fieldType === "number[]" || fieldType === "boolean[]") { return true; } } From 6ab987e62782674ba2d364b2af423919aa56e233 Mon Sep 17 00:00:00 2001 From: cs01 Date: Sat, 21 Mar 2026 00:18:36 -0700 Subject: [PATCH 6/6] restore boolean in isArrayExpression to prevent misclassification in native compiler on linux --- src/codegen/infrastructure/type-inference.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/codegen/infrastructure/type-inference.ts b/src/codegen/infrastructure/type-inference.ts index 69818197..6bbe02d3 100644 --- a/src/codegen/infrastructure/type-inference.ts +++ b/src/codegen/infrastructure/type-inference.ts @@ -1335,7 +1335,11 @@ export class TypeInference { } if (e.type === "variable" || e.type === "index_access") { const resolved = this.resolveExpressionType(expr); - if (resolved && resolved.arrayDepth > 0 && resolved.base === "number") { + if ( + resolved && + resolved.arrayDepth > 0 && + (resolved.base === "number" || resolved.base === "boolean") + ) { return true; } if (e.type === "index_access") return false;