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
84 changes: 83 additions & 1 deletion arimo/compiler/backend/IRLower.arm
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,20 @@ public class IRLower {
private allFieldClasses: List<String>;
private classFldStarts : List<Integer>;
private classFldCounts : List<Integer>;
private allStaticFieldNames : List<String>;
private classStaticFldStarts : List<Integer>;
private classStaticFldCounts : List<Integer>;
private methodRetNames : List<String>;
private methodRetClasses: List<String>;
private teardownLabels : List<String>;
private curClass : String;
private thisReg : String;
private linux : Boolean;
private loweringError : Boolean;

public constructor(linux: Boolean) {
this.linux = linux;
this.loweringError = false;
this.irFns = List();
this.strNames = List();
this.strConts = List();
Expand All @@ -98,6 +103,9 @@ public class IRLower {
this.allFieldClasses = List();
this.classFldStarts = List();
this.classFldCounts = List();
this.allStaticFieldNames = List();
this.classStaticFldStarts = List();
this.classStaticFldCounts = List();
this.methodRetNames = List();
this.methodRetClasses = List();
this.teardownLabels = List();
Expand All @@ -106,6 +114,8 @@ public class IRLower {
this.irFns.append(IRFunction("__placeholder", IRType.VOID));
}

public hasLoweringError() : Boolean { return this.loweringError; }

// ===== Class layout registration =====

private registerClasses(modules: List<ArimoModule>) {
Expand All @@ -128,19 +138,26 @@ public class IRLower {
if (this.classIdxOf(cd.name) >= 0) { return; }
Integer start = this.allFieldNames.length();
Integer count = 0;
Integer stStart = this.allStaticFieldNames.length();
Integer stCount = 0;
Integer fi = 0;
while (fi < cd.fields.length()) {
FieldDecl fd = cd.fields.get(fi) as FieldDecl;
if (!fd.isStatic) {
this.allFieldNames.append(fd.name);
this.allFieldClasses.append(this.tyToClass(fd.ty));
count = count + 1;
} else {
this.allStaticFieldNames.append(fd.name);
stCount = stCount + 1;
}
fi = fi + 1;
}
this.classNames.append(cd.name);
this.classFldStarts.append(start);
this.classFldCounts.append(count);
this.classStaticFldStarts.append(stStart);
this.classStaticFldCounts.append(stCount);
this.teardownLabels.append("");
// Track parent class for recursive field release (inheritance chain)
if (cd.extends_ != "" && cd.extends_.length() > 0) {
Expand Down Expand Up @@ -543,6 +560,14 @@ public class IRLower {
}

private emitFieldPtr(objVal: IRValue, offset: Integer) : IRValue {
if (objVal.kind == IRValueKind.IMM_INT && objVal.immInt == 0) {
IO.println("arc: [FAIL] SafeRegAlloc null field base offset=${offset} class=${this.curClass}");
this.loweringError = true;
String dummyR = this.newReg();
IRValue dummyV = IRValue.reg(dummyR, IRType.PTR);
this.emit(IRInstr.mov(dummyV, IRValue.ofInt(1, IRType.I64)));
return dummyV;
}
String pReg = this.newReg();
IRValue pDst = IRValue.reg(pReg, IRType.PTR);
// Compute field pointer via MOV + ADD
Expand Down Expand Up @@ -988,6 +1013,28 @@ public class IRLower {
}

if (expr.kind == ExprKind.FIELD) {
if (expr.object.kind == ExprKind.IDENT) {
Integer stClsIdx = this.classIdxOf(expr.object.strVal);
if (stClsIdx >= 0) {
Integer stFldIdx = this.fieldIdxOf(stClsIdx, expr.field);
if (stFldIdx >= 0) {
String stR = this.newReg();
this.emit(IRInstr.mov(IRValue.reg(stR, IRType.I64), IRValue.ofInt(stFldIdx, IRType.I64)));
return IRValue.reg(stR, IRType.I64);
}
Integer stStart = this.classStaticFldStarts.get(stClsIdx) as Integer;
Integer stCount = this.classStaticFldCounts.get(stClsIdx) as Integer;
Integer sfi = 0;
while (sfi < stCount) {
if (this.allStaticFieldNames.get(stStart + sfi) as String == expr.field) {
String stR = this.newReg();
this.emit(IRInstr.mov(IRValue.reg(stR, IRType.I64), IRValue.ofInt(sfi, IRType.I64)));
return IRValue.reg(stR, IRType.I64);
}
sfi = sfi + 1;
}
}
}
IRValue objVal = this.lowerExpr(expr.object);
String objCls = this.inferClass(expr.object);
Integer clsIdx = this.classIdxOf(objCls);
Expand Down Expand Up @@ -1607,6 +1654,11 @@ public class IRLower {
if (!libcRes.isNone()) { return libcRes; }
}
Integer clsIdx = this.classIdxOf(class_);
// If class_ is not a registered class, parser generated CTOR for what
// is actually a static method call on the current class (e.g. foo() → T.foo()).
if (clsIdx < 0 && this.curClass != "") {
return this.lowerStaticCall(this.curClass, class_, args);
}
Integer sz = 16;
if (clsIdx >= 0) { sz = this.classSizeBytes(clsIdx); }
IRValue objPtr = this.emitHeapAlloc(IRValue.ofInt(sz, IRType.I64));
Expand Down Expand Up @@ -1637,8 +1689,15 @@ public class IRLower {
if (obj.strVal == "Env") {
return this.lowerStaticCall("Env", method, args);
}
if (obj.strVal == "File") {
return this.lowerStaticCall("File", method, args);
}
if (obj.strVal == "Process") {
return this.lowerStaticCall("Process", method, args);
}
// Static method call on known class: ClassName.method()
if (this.classIdxOf(obj.strVal) >= 0) {
// Guarded by varLookup — only redirect if name is NOT a local variable.
if (this.varLookup(obj.strVal) == "" && this.classIdxOf(obj.strVal) >= 0) {
return this.lowerStaticCall(obj.strVal, method, args);
}
}
Expand Down Expand Up @@ -3000,6 +3059,7 @@ public class IRLower {
this.generateToLower();
this.generateSystem();
this.generateBuildArgs();
this.generateFileExists();
this.generateStartFn();
}

Expand Down Expand Up @@ -3186,4 +3246,26 @@ public class IRLower {
this.emit(IRInstr.label("sy_err"));
this.emit(IRInstr.ret(IRValue.ofInt(-1, IRType.I64)));
}

private generateFileExists() {
this.beginFn("File__exists", IRType.I64);
this.addParamToLast("path", IRType.PTR);
this.resetFnContext(); this.emit(IRInstr.label("entry"));
IRValue pathV = IRValue.reg("path", IRType.PTR);
String modeR = this.newReg(); IRValue modeV = IRValue.reg(modeR, IRType.PTR);
this.emit(IRInstr.mov(modeV, IRValue.global(this.internStr("r"))));
List<IRValue> foArgs = List(); foArgs.append(pathV); foArgs.append(modeV);
String fR = this.newReg(); IRValue fV = IRValue.reg(fR, IRType.I64);
this.emit(IRInstr.call(fV, "__arimo_fopen", foArgs));
String eqL = this.newLabel("fe_fail"); String endL = this.newLabel("fe_end");
String retR = this.newReg(); IRValue retV = IRValue.reg(retR, IRType.I64);
this.emit(IRInstr.cmp(fV, IRValue.ofInt(0, IRType.I64)));
this.emit(IRInstr.branch(IROpcode.JE, eqL));
this.emit(IRInstr.mov(retV, IRValue.ofInt(1, IRType.I64)));
this.emit(IRInstr.jmp(endL));
this.emit(IRInstr.label(eqL));
this.emit(IRInstr.mov(retV, IRValue.ofInt(0, IRType.I64)));
this.emit(IRInstr.label(endL));
this.emit(IRInstr.ret(retV));
}
}
7 changes: 7 additions & 0 deletions arimo/compiler/backend/IRToX64.arm
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ public class IRToX64 {
this.safeRa.assertKnown(val.name);
this.safeRa.assertWritten(val.name);
Integer off = this.safeRa.slotOffsetByName(val.name);
if (off == 0) { this.safeRa.errorCode = 3; }
this.enc.movRMem(scratchReg, X64Reg.RBP, off);
return scratchReg;
}
Expand All @@ -182,6 +183,7 @@ public class IRToX64 {
private safeStoreDst(name: String, srcReg: Integer) {
this.safeRa.assertKnown(name);
Integer off = this.safeRa.slotOffsetByName(name);
if (off == 0) { this.safeRa.errorCode = 3; }
this.enc.movMemR(X64Reg.RBP, off, srcReg);
this.safeRa.markValueWritten(name);
}
Expand Down Expand Up @@ -279,6 +281,11 @@ public class IRToX64 {
this.curName = fn.name;
this.savedCount = 0; // no callee-saved tracking in safe mode

// DEBUG: flag large frames (potential slot explosion)
if (frameSize > 1024) {
IO.println("SAFEDBG BIGFRAME fn=${fn.name} slots=${this.safeRa.totalSlots()} frame=${frameSize}");
}

this.enc.defineLabel(fn.name);

// _start raw prologue (Linux): capture argc/argv from initial stack
Expand Down
19 changes: 12 additions & 7 deletions arimo/compiler/backend/SafeRegAlloc.arm
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public class SafeRegAlloc {
private sci : Integer;
private curFn : String;
private dirty : Boolean;
public errorCode : Integer; // 0=ok, 1=unknown slot, 2=unwritten slot, 3=offset zero

public constructor() {
this.slots = List();
Expand All @@ -59,6 +60,7 @@ public class SafeRegAlloc {
this.sci = 0;
this.curFn = "";
this.dirty = false;
this.errorCode = 0;
// Scratch pool: caller-saved regs for instruction-local use
// RAX excluded — reserved for idiv/call-return/syscall-num
this.scratch.append(X64Reg.RDI);
Expand Down Expand Up @@ -137,9 +139,14 @@ public class SafeRegAlloc {
}

// slotOffsetByName: stack offset looked up by value name.
// Returns offset below frame (safe sentinel) for unknown names — never 0.
public slotOffsetByName(name: String) : Integer {
Integer idx = this.getSlot(name);
if (idx < 0) { return 0; }
if (idx < 0) {
this.errorCode = 1;
Integer sentinel = -(this.slotCnt + 100) * 8;
return sentinel;
}
return this.slotOffset(idx);
}

Expand Down Expand Up @@ -239,29 +246,27 @@ public class SafeRegAlloc {
public assertKnown(name: String) {
Integer idx = this.getSlot(name);
if (idx < 0) {
IO.println("arc: [FAIL] SafeRegAlloc: unknown value \"${name}\" in fn ${this.curFn} — no slot allocated");
// In V1 self-host: mark error state for caller to check
this.errorCode = 1;
}
}

// assertKnownIdx: index-based variant.
public assertKnownIdx(idx: Integer) {
if (idx < 0 || idx >= this.slots.length()) {
IO.println("arc: [FAIL] SafeRegAlloc: bad slot index ${idx} in fn ${this.curFn} — out of range (0..${this.slots.length() - 1})");
this.errorCode = 1;
}
}

// assertWritten: hard-fail if slot exists but never written to.
// Prevents reloading uninitialized stack slots (e.g., used before def).
public assertWritten(name: String) {
Integer idx = this.getSlot(name);
if (idx < 0) {
IO.println("arc: [FAIL] SafeRegAlloc: assertWritten unknown value \"${name}\" in fn ${this.curFn}");
this.errorCode = 2;
return;
}
SafeValueSlot sv = this.slots.get(idx) as SafeValueSlot;
if (!sv.written) {
IO.println("arc: [FAIL] SafeRegAlloc: reload of unwritten slot ${sv.slot} (value \"${name}\") in fn ${this.curFn}");
this.errorCode = 2;
}
}

Expand Down
Loading