Skip to content
Closed
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
5 changes: 4 additions & 1 deletion src/codegen/infrastructure/function-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export interface FunctionGeneratorContext {
getTargetOS(): string;
setRawInterfaceType(name: string, type: string): void;
getUsesMathRandom(): boolean;
getUsesGC(): boolean;
}

export class FunctionGenerator {
Expand Down Expand Up @@ -1026,7 +1027,9 @@ export class FunctionGenerator {
" {\n";
ir += "entry:\n";

ir += " call void @GC_init()\n";
if (this.ctx.getUsesGC()) {
ir += " call void @GC_init()\n";
}
if (this.ctx.getUsesMathRandom()) {
ir += " %__seed_time = call i64 @time(i8* null)\n";
ir += " call void @srand48(i64 %__seed_time)\n";
Expand Down
19 changes: 11 additions & 8 deletions src/codegen/infrastructure/llvm-declarations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface DeclConfig {
gc?: boolean;
curl?: boolean;
crypto?: boolean;
sqlite?: boolean;
Expand All @@ -25,14 +26,16 @@ export function getLLVMDeclarations(config?: DeclConfig): string {
ir += "declare i8* @calloc(i64, i64)\n";
ir += "declare void @free(i8*)\n";

ir += "; Boehm GC - automatic garbage collection\n";
ir += "declare void @GC_init()\n";
ir += "declare noalias i8* @GC_malloc(i64)\n";
ir += "declare noalias i8* @GC_malloc_atomic(i64)\n";
ir += "declare noalias i8* @GC_malloc_uncollectable(i64)\n";
ir += "declare i8* @GC_realloc(i8*, i64)\n";
ir += "declare void @GC_disable()\n";
ir += "declare void @GC_enable()\n";
if (config?.gc) {
ir += "; Boehm GC - automatic garbage collection\n";
ir += "declare void @GC_init()\n";
ir += "declare noalias i8* @GC_malloc(i64)\n";
ir += "declare noalias i8* @GC_malloc_atomic(i64)\n";
ir += "declare noalias i8* @GC_malloc_uncollectable(i64)\n";
ir += "declare i8* @GC_realloc(i8*, i64)\n";
ir += "declare void @GC_disable()\n";
ir += "declare void @GC_enable()\n";
}
ir += "declare i8* @strcpy(i8*, i8*)\n";
ir += "declare i8* @strncpy(i8*, i8*, i64)\n";
ir += "declare i8* @strcat(i8*, i8*)\n";
Expand Down
186 changes: 161 additions & 25 deletions src/codegen/llvm-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,13 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {

public usesGC: number = 0;
public usesMathRandom: number = 0;
public usesOs: number = 0;
public usesDoubleToString: number = 0;
public usesPath: number = 0;
public usesFs: number = 0;
public usesBase64Bridge: number = 0;
public usesUrlBridge: number = 0;
public usesUriBridge: number = 0;

private dbgAlloc(): number {
const id = this.dbgNextId;
Expand Down Expand Up @@ -324,18 +331,20 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
}

private dbgInit(sourceFilePath: string): void {
let lastSlash = -1;
for (let i = 0; i < sourceFilePath.length; i++) {
if (sourceFilePath.charAt(i) === "/") {
lastSlash = i;
let lastSlash: number = 0;
let foundSlash = false;
let pos: number = 0;
let ch = sourceFilePath.charAt(pos);
while (ch !== "") {
if (ch === "/") {
lastSlash = pos;
foundSlash = true;
}
pos = pos + 1;
ch = sourceFilePath.charAt(pos);
}
let filename = sourceFilePath;
let directory = ".";
if (lastSlash >= 0) {
filename = sourceFilePath.substring(lastSlash + 1);
directory = sourceFilePath.substring(0, lastSlash);
}
const filename = foundSlash ? sourceFilePath.slice(lastSlash + 1) : sourceFilePath;
const directory = foundSlash ? sourceFilePath.slice(0, lastSlash) : ".";

this.dbgFileId = this.dbgAlloc();
this.dbgSetNode(
Expand Down Expand Up @@ -691,7 +700,8 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
name: string,
): { keys: string[]; types: string[]; tsTypes: string[] } | null {
if (!this.ast || !this.ast.interfaces) return null;
const baseName = name.endsWith("?") ? name.slice(0, -1) : name;
const qIdx = name.indexOf("?");
const baseName = qIdx >= 0 ? name.slice(0, qIdx) : name;
const cleanName = baseName.indexOf(" | ") !== -1 ? baseName.split(" | ")[0] : baseName;
const keys: string[] = [];
const types: string[] = [];
Expand All @@ -706,7 +716,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
if (!field) continue;
let fieldName = field.name;
if (fieldName.endsWith("?")) {
fieldName = fieldName.slice(0, -1);
fieldName = fieldName.slice(0, fieldName.indexOf("?"));
}
keys.push(fieldName);
// field.type is a TS type (e.g. "string", "number") — convert to LLVM for types[]
Expand Down Expand Up @@ -811,7 +821,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
if (!f) continue;
let fName = f.name;
if (fName.endsWith("?")) {
fName = fName.slice(0, -1);
fName = fName.slice(0, fName.indexOf("?"));
}
if (fName === fieldName) {
return f.type;
Expand Down Expand Up @@ -1010,7 +1020,6 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
public setUsesCurl(value: boolean): void {
this.usesCurl = value ? 1 : 0;
}
public setUsesOs(_value: boolean): void {}
public getUsesCurl(): boolean {
return this.usesCurl !== 0;
}
Expand Down Expand Up @@ -1101,6 +1110,21 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
public setUsesMathRandom(value: boolean): void {
this.usesMathRandom = value ? 1 : 0;
}
public setUsesOs(value: boolean): void {
this.usesOs = value ? 1 : 0;
}
public getUsesOs(): boolean {
return this.usesOs !== 0;
}
public getUsesBase64Bridge(): boolean {
return this.usesBase64Bridge !== 0;
}
public getUsesUrlBridge(): boolean {
return this.usesUrlBridge !== 0;
}
public getUsesUriBridge(): boolean {
return this.usesUriBridge !== 0;
}
public setCurrentDeclaredInterfaceType(type: string | undefined): void {
this.currentDeclaredInterfaceType = type;
}
Expand Down Expand Up @@ -1498,6 +1522,13 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
this.usesAsyncFs = 0;
this.usesGC = 0;
this.usesMathRandom = 0;
this.usesOs = 0;
this.usesDoubleToString = 0;
this.usesPath = 0;
this.usesFs = 0;
this.usesBase64Bridge = 0;
this.usesUrlBridge = 0;
this.usesUriBridge = 0;

this.ast = ast;

Expand Down Expand Up @@ -1662,7 +1693,7 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
)
continue;

let semaIdx = -1;
let semaIdx: number = -1;
for (let si = 0; si < this.semaSymbolCount; si++) {
if (this.semaSymbolNames[si] === name) {
semaIdx = si;
Expand Down Expand Up @@ -1769,6 +1800,43 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
this.stringBuilderScap.clear();
}

emit(instruction: string): void {
if (!this.usesGC && instruction.includes("@GC_")) {
this.usesGC = 1;
}
if (!this.usesDoubleToString && instruction.includes("@__double_to_string(")) {
this.usesDoubleToString = 1;
}
if (!this.usesPath && instruction.includes("@__path_")) {
this.usesPath = 1;
}
if (!this.usesFs && instruction.includes("@__fs_")) {
this.usesFs = 1;
}
if (
!this.usesBase64Bridge &&
(instruction.includes("@cs_btoa(") ||
instruction.includes("@cs_atob(") ||
instruction.includes("@cs_base64_decode("))
) {
this.usesBase64Bridge = 1;
}
if (
!this.usesUrlBridge &&
(instruction.includes("@cs_url_") || instruction.includes("@cs_urlsearch_"))
) {
this.usesUrlBridge = 1;
}
if (
!this.usesUriBridge &&
(instruction.includes("@cs_encode_uri_component(") ||
instruction.includes("@cs_decode_uri_component("))
) {
this.usesUriBridge = 1;
}
super.emit(instruction);
}

getThisPointer(): string | null {
return this.thisPointer;
}
Expand Down Expand Up @@ -2505,21 +2573,11 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
if (safeStr) {
irParts.push(safeStr);
}
const dblToStr = getDoubleToStringHelper();
if (dblToStr) {
irParts.push(dblToStr);
}
const strHash = getStringHashHelper();
if (strHash) {
irParts.push(strHash);
}

irParts.push(this.fsGen.generateReaddirSyncHelper());
irParts.push(this.fsGen.generateStatSyncHelper());
irParts.push(this.pathGen.generateNormalizeHelper());
irParts.push(this.pathGen.generateRelativeHelper());
irParts.push(this.pathGen.generateParseHelper());

const globalVars = getGlobalVariables();
if (globalVars) {
irParts.push(globalVars);
Expand Down Expand Up @@ -2551,6 +2609,23 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
}
}

if (!this.usesGC) {
for (let i = 0; i < irParts.length; i++) {
if (irParts[i].includes("@GC_")) {
this.usesGC = 1;
break;
}
}
}
if (!this.usesGC) {
for (let i = 0; i < this.globalStrings.length; i++) {
if (this.globalStrings[i].includes("@GC_")) {
this.usesGC = 1;
break;
}
}
}

const mainIr = this.generateMain();

const liftedFunctions = this.exprGen.arrowFunctionGen.getLiftedFunctions();
Expand Down Expand Up @@ -2714,6 +2789,60 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
irParts.push(this.embedGen.generateLengthLookupFunction());
}

for (let psi = 0; psi < irParts.length; psi++) {
const part = irParts[psi];
if (!this.usesGC && part.includes("@GC_")) {
this.usesGC = 1;
}
if (!this.usesDoubleToString && part.includes("@__double_to_string(")) {
this.usesDoubleToString = 1;
}
if (!this.usesPath && part.includes("@__path_")) {
this.usesPath = 1;
}
if (!this.usesFs && part.includes("@__fs_")) {
this.usesFs = 1;
}
if (
!this.usesBase64Bridge &&
(part.includes("@cs_btoa(") ||
part.includes("@cs_atob(") ||
part.includes("@cs_base64_decode("))
) {
this.usesBase64Bridge = 1;
}
if (!this.usesUrlBridge && (part.includes("@cs_url_") || part.includes("@cs_urlsearch_"))) {
this.usesUrlBridge = 1;
}
if (
!this.usesUriBridge &&
(part.includes("@cs_encode_uri_component(") || part.includes("@cs_decode_uri_component("))
) {
this.usesUriBridge = 1;
}
}
for (let gsi = 0; gsi < this.globalStrings.length; gsi++) {
if (!this.usesGC && this.globalStrings[gsi].includes("@GC_")) {
this.usesGC = 1;
}
}

if (this.usesDoubleToString) {
this.usesGC = 1;
irParts.push(getDoubleToStringHelper());
}
if (this.usesFs) {
this.usesGC = 1;
irParts.push(this.fsGen.generateReaddirSyncHelper());
irParts.push(this.fsGen.generateStatSyncHelper());
}
if (this.usesPath) {
this.usesGC = 1;
irParts.push(this.pathGen.generateNormalizeHelper());
irParts.push(this.pathGen.generateRelativeHelper());
irParts.push(this.pathGen.generateParseHelper());
}

const needsLibuv =
this.usesTimers ||
this.usesPromises ||
Expand All @@ -2732,6 +2861,8 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
finalParts.push("\n");
}

finalParts.push("%PathParseResult = type { i8*, i8*, i8*, i8*, i8* }\n\n");

finalParts.push("; Tree-sitter type definitions\n");
finalParts.push("%TSParser = type opaque\n");
finalParts.push("%TSTree = type opaque\n");
Expand Down Expand Up @@ -2762,9 +2893,14 @@ export class LLVMGenerator extends BaseGenerator implements IGeneratorContext {
finalParts.push("\n");
}

if (this.usesStringBuilder || this.usesCurl) {
this.usesGC = 1;
}

finalParts.push(
this.filterDuplicateDeclarations(
getLLVMDeclarations({
gc: this.usesGC !== 0,
curl: this.usesCurl !== 0,
crypto: this.usesCrypto !== 0,
sqlite: this.usesSqlite !== 0,
Expand Down
1 change: 1 addition & 0 deletions src/codegen/stdlib/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ export class JsonGenerator {
return;
}

this.ctx.setUsesGC(true);
const fieldCount = this.ctx.interfaceStructGenGetFieldCount(typeName);

for (let fi = 0; fi < fieldCount; fi++) {
Expand Down
2 changes: 1 addition & 1 deletion src/codegen/stdlib/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,9 +671,9 @@ export class PathGenerator {
}

// %PathParseResult = { root, dir, base, name, ext } — all i8*
// Type definition emitted separately in llvm-generator.ts finalParts so it precedes all GEPs
generateParseHelper(): string {
let ir = "";
ir += "%PathParseResult = type { i8*, i8*, i8*, i8*, i8* }\n\n";
ir += "define i8* @__path_parse(i8* %path) {\n";
ir += "entry:\n";
ir += " %len = call i64 @strlen(i8* %path)\n";
Expand Down
15 changes: 11 additions & 4 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,14 @@ export function compile(
const treeSitterPath = sdk ? sdk.vendorPath : TREESITTER_LIB_PATH;

const platformLibs = targetIsMac ? "" : " -lm -ldl -lrt -lpthread";
let linkLibs = `-L${gcPath} -lgc` + platformLibs;
const needsGcLib =
generator.usesGC ||
generator.usesRegex ||
generator.usesBase64Bridge ||
generator.usesUrlBridge ||
generator.usesUriBridge ||
generator.usesStringBuilder;
let linkLibs = needsGcLib ? `-L${gcPath} -lgc` + platformLibs : platformLibs.trimStart();
if (generator.usesJson) {
linkLibs += ` -L${yyjsonPath} -lyyjson`;
}
Expand Down Expand Up @@ -404,9 +411,9 @@ export function compile(
const cpBridgeObj = generator.usesChildProcess ? `${bridgePath}/child-process-bridge.o` : "";
const osBridgeObj = `${bridgePath}/os-bridge.o`;
const timeBridgeObj = `${bridgePath}/time-bridge.o`;
const base64BridgeObj = `${bridgePath}/base64-bridge.o`;
const urlBridgeObj = `${bridgePath}/url-bridge.o`;
const uriBridgeObj = `${bridgePath}/uri-bridge.o`;
const base64BridgeObj = generator.usesBase64Bridge ? `${bridgePath}/base64-bridge.o` : "";
const urlBridgeObj = generator.usesUrlBridge ? `${bridgePath}/url-bridge.o` : "";
const uriBridgeObj = generator.usesUriBridge ? `${bridgePath}/uri-bridge.o` : "";
const dotenvBridgeObj = fs.existsSync(`${bridgePath}/dotenv-bridge.o`)
? `${bridgePath}/dotenv-bridge.o`
: "";
Expand Down
Loading
Loading