diff --git a/Main.arm b/Main.arm
index e2bac4f..6606661 100644
--- a/Main.arm
+++ b/Main.arm
@@ -215,8 +215,8 @@ public class Main {
IO.println("arc: run: cd ".concat(name).concat(" && arc build"));
}
- private static compile(inputPath: String, stdlibPath: String, checkOnly: Boolean, outName: String, isLinux: Boolean) : Void {
- if (!File.exists(inputPath)) {
+ private static compile(inputPath: String, stdlibPath: String, checkOnly: Boolean, outName: String, isLinux: Boolean, safeRegAlloc: Boolean) : Void {
+ if (File.exists(inputPath) == false) {
IO.println("arc: file not found: ${inputPath}");
return;
}
@@ -335,6 +335,10 @@ public class Main {
IO.println("arc: generating native code...");
NativeBackend nb = NativeBackend(isLinux);
+ if (safeRegAlloc) {
+ nb.setSafeRegAlloc(true);
+ IO.println("arc: using SafeRegAlloc (stack-canonical, correctness-first)");
+ }
nb.compile(modules, exeFile);
if (isLinux) { Process.exec("chmod +x ".concat(exeFile)); }
IO.println("arc: done -> ${exeFile}");
@@ -353,14 +357,16 @@ public class Main {
String stdlibPath = Main.findStdlib(exePath);
String cmd = argv.get(1);
- Boolean checkOnly = false;
- Boolean doRun = false;
- Boolean isLinux = Env.platform().compareTo("linux") == 0;
+ Boolean checkOnly = false;
+ Boolean doRun = false;
+ Boolean isLinux = Env.platform().compareTo("linux") == 0;
+ Boolean safeRegAlloc = false;
Integer fi = 2;
while (fi < argc) {
String flag = argv.get(fi);
- if (flag.compareTo("--check") == 0) { checkOnly = true; }
+ if (flag.compareTo("--check") == 0) { checkOnly = true; }
+ if (flag.compareTo("--safe-regalloc") == 0) { safeRegAlloc = true; }
if (flag.compareTo("--stdlib-path") == 0) {
fi++;
if (fi < argc) { stdlibPath = argv.get(fi); }
@@ -391,7 +397,7 @@ public class Main {
if (cmd.compareTo("run") == 0) { doRun = true; }
if (cmd.compareTo("check") == 0) { checkOnly = true; }
- if (!File.exists("arc.toml")) {
+ if (File.exists("arc.toml") == false) {
IO.println("arc: arc.toml not found - run from project root");
return;
}
@@ -402,7 +408,7 @@ public class Main {
if (projName.length() == 0) { projName = "app"; }
IO.println("arc v1.0 - building '${projName}'");
- Main.compile(entry, stdlibPath, checkOnly, projName, isLinux);
+ Main.compile(entry, stdlibPath, checkOnly, projName, isLinux, safeRegAlloc);
if (doRun && !checkOnly) {
String runCmd = ".\\".concat(projName).concat(".exe");
@@ -415,7 +421,7 @@ public class Main {
if (cmd.endsWith(".arm")) {
IO.println("arc v1.0 - compiling '${cmd}'");
- Main.compile(cmd, stdlibPath, checkOnly, "", isLinux);
+ Main.compile(cmd, stdlibPath, checkOnly, "", isLinux, safeRegAlloc);
return;
}
diff --git a/arimo/compiler/backend/IRLower.arm b/arimo/compiler/backend/IRLower.arm
index 36f0024..03d49f5 100644
--- a/arimo/compiler/backend/IRLower.arm
+++ b/arimo/compiler/backend/IRLower.arm
@@ -1,2831 +1,3189 @@
-/*
-Arimo Lang Compiler - A modern programming language and compiler
-Copyright (C) 2026 Egecan Akıncıoğlu
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published
-by the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see .
-*/
-
-package arimo.compiler.backend;
-
-import arimo.compiler.ast.ArimoModule;
-import arimo.compiler.ast.item.Item;
-import arimo.compiler.ast.item.ItemKind;
-import arimo.compiler.ast.item.ClassDecl;
-import arimo.compiler.ast.decl.MethodDecl;
-import arimo.compiler.ast.decl.FieldDecl;
-import arimo.compiler.ast.decl.ConstructorDecl;
-import arimo.compiler.ast.decl.Param;
-import arimo.compiler.ast.stmt.Stmt;
-import arimo.compiler.ast.stmt.StmtKind;
-import arimo.compiler.ast.stmt.ElseIfBranch;
-import arimo.compiler.ast.expr.Expr;
-import arimo.compiler.ast.expr.ExprKind;
-import arimo.compiler.ast.expr.BinaryOp;
-import arimo.compiler.ast.expr.StrPart;
-import arimo.compiler.ast.types.AstType;
-import arimo.compiler.ast.types.TypeKind;
-import arimo.compiler.backend.IRFunction;
-import arimo.compiler.backend.IRInstr;
-import arimo.compiler.backend.IRValue;
-import arimo.compiler.backend.IRValueKind;
-import arimo.compiler.backend.IROpcode;
-import arimo.compiler.backend.IRType;
-
-public class IRLower {
- public irFns : List;
- public strNames : List;
- public strConts : List;
- private strCnt : Integer;
- private labelCnt : Integer;
- private regCnt : Integer;
- private varNames : List;
- private varRegs : List;
- private varClassNames : List;
- private breakLbl : String;
- private contLbl : String;
- private breakDepth : Integer;
- private contDepth : Integer;
- private scopeStack : List>;
- private mainFn : String;
- private classNames : List;
- private classParents : List;
- private allFieldNames : List;
- private allFieldClasses: List;
- private classFldStarts : List;
- private classFldCounts : List;
- private methodRetNames : List;
- private methodRetClasses: List;
- private teardownLabels : List;
- private curClass : String;
- private thisReg : String;
- private linux : Boolean;
-
- public constructor(linux: Boolean) {
- this.linux = linux;
- this.irFns = List();
- this.strNames = List();
- this.strConts = List();
- this.strCnt = 0;
- this.labelCnt = 0;
- this.regCnt = 0;
- this.varNames = List();
- this.varRegs = List();
- this.varClassNames = List();
- this.breakLbl = "";
- this.contLbl = "";
- this.breakDepth = 0;
- this.contDepth = 0;
- this.scopeStack = List();
- this.mainFn = "";
- this.classNames = List();
- this.classParents = List();
- this.allFieldNames = List();
- this.allFieldClasses = List();
- this.classFldStarts = List();
- this.classFldCounts = List();
- this.methodRetNames = List();
- this.methodRetClasses = List();
- this.teardownLabels = List();
- this.curClass = "";
- this.thisReg = "";
- this.irFns.append(IRFunction("__placeholder", IRType.VOID));
- }
-
- // ===== Class layout registration =====
-
- private registerClasses(modules: List) {
- Integer mi = 0;
- while (mi < modules.length()) {
- ArimoModule m = modules.get(mi) as ArimoModule;
- Integer ii = 0;
- while (ii < m.items.length()) {
- Item it = m.items.get(ii) as Item;
- if (it.kind == ItemKind.CLASS) {
- this.registerClass(it.classDecl);
- }
- ii = ii + 1;
- }
- mi = mi + 1;
- }
- }
-
- private registerClass(cd: ClassDecl) {
- if (this.classIdxOf(cd.name) >= 0) { return; }
- Integer start = this.allFieldNames.length();
- Integer count = 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;
- }
- fi = fi + 1;
- }
- this.classNames.append(cd.name);
- this.classFldStarts.append(start);
- this.classFldCounts.append(count);
- this.teardownLabels.append("");
- // Track parent class for recursive field release (inheritance chain)
- if (cd.extends_ != "" && cd.extends_.length() > 0) {
- this.classParents.append(this.classIdxOf(cd.extends_));
- } else {
- this.classParents.append(-1);
- }
-
- // Track method return types for inferClass(METHOD)
- Integer mi = 0;
- while (mi < cd.methods.length()) {
- MethodDecl md = cd.methods.get(mi) as MethodDecl;
- String retCls = this.tyToClass(md.returnTy);
- this.methodRetNames.append("${cd.name}__${md.name}");
- this.methodRetClasses.append(retCls);
- mi = mi + 1;
- }
- }
-
- private classIdxOf(name: String) : Integer {
- Integer i = 0;
- while (i < this.classNames.length()) {
- String n = this.classNames.get(i) as String;
- if (n == name) { return i; }
- i = i + 1;
- }
- return -1;
- }
-
- private fieldIdxOf(clsIdx: Integer, fieldName: String) : Integer {
- Integer start = this.classFldStarts.get(clsIdx) as Integer;
- Integer count = this.classFldCounts.get(clsIdx) as Integer;
- Integer i = 0;
- while (i < count) {
- String fn = this.allFieldNames.get(start + i) as String;
- if (fn == fieldName) { return i + 1; }
- i = i + 1;
- }
- return -1;
- }
-
- private classSizeBytes(clsIdx: Integer) : Integer {
- Integer count = this.classFldCounts.get(clsIdx) as Integer;
- return (1 + count) * 8 + 8;
- }
-
- private refCountOff(clsIdx: Integer) : Integer {
- return this.classSizeBytes(clsIdx) - 8;
- }
-
- // ===== Variable class tracking =====
-
- private varClassOf(name: String) : String {
- Integer i = 0;
- while (i < this.varNames.length()) {
- String n = this.varNames.get(i) as String;
- if (n == name) { return this.varClassNames.get(i) as String; }
- i = i + 1;
- }
- return "";
- }
-
- private varSetClass(name: String, cls: String) {
- Integer i = 0;
- while (i < this.varNames.length()) {
- String n = this.varNames.get(i) as String;
- if (n == name) { this.varClassNames.set(i, cls); return; }
- i = i + 1;
- }
- }
-
- // ===== Type inference =====
-
- private tyToClass(ty: AstType) : String {
- if (ty == null) { return ""; }
- if (ty.kind == TypeKind.NAMED) { return ty.name; }
- if (ty.kind == TypeKind.STR) { return "String"; }
- if (ty.kind == TypeKind.LIST) { return "List"; }
- if (ty.kind == TypeKind.NULLABLE) { return this.tyToClass(ty.inner); }
- if (ty.kind == TypeKind.INTEGER) { return "Integer"; }
- if (ty.kind == TypeKind.BOOLEAN) { return "Boolean"; }
- if (ty.kind == TypeKind.FLOAT) { return "Float"; }
- if (ty.kind == TypeKind.CHAR) { return "Char"; }
- if (ty.kind == TypeKind.I64) { return "I64"; }
- if (ty.kind == TypeKind.I32) { return "I32"; }
- if (ty.kind == TypeKind.I16) { return "I16"; }
- if (ty.kind == TypeKind.I8) { return "I8"; }
- if (ty.kind == TypeKind.U64) { return "U64"; }
- if (ty.kind == TypeKind.U32) { return "U32"; }
- if (ty.kind == TypeKind.U16) { return "U16"; }
- if (ty.kind == TypeKind.U8) { return "U8"; }
- return "";
- }
-
- private inferClass(expr: Expr) : String {
- if (expr.kind == ExprKind.THIS) { return this.curClass; }
- if (expr.kind == ExprKind.IDENT) { return this.varClassOf(expr.strVal); }
- if (expr.kind == ExprKind.CTOR) { return expr.class_; }
- if (expr.kind == ExprKind.CAST) {
- if (expr.castTy.kind == TypeKind.NAMED) { return expr.castTy.name; }
- if (expr.castTy.kind == TypeKind.STR) { return "String"; }
- if (expr.castTy.kind == TypeKind.LIST) { return "List"; }
- if (expr.castTy.kind == TypeKind.NULLABLE) { return this.tyToClass(expr.castTy.inner); }
- }
- if (expr.kind == ExprKind.FIELD) {
- String objCls = this.inferClass(expr.object);
- Integer clsIdx = this.classIdxOf(objCls);
- if (clsIdx >= 0) {
- Integer fldIdx = this.fieldIdxOf(clsIdx, expr.field);
- if (fldIdx >= 0) {
- Integer start = this.classFldStarts.get(clsIdx) as Integer;
- return this.allFieldClasses.get(start + fldIdx - 1) as String;
- }
- }
- return "";
- }
- if (expr.kind == ExprKind.METHOD) {
- String objCls = this.inferClass(expr.object);
- if (objCls != "") {
- String key = "${objCls}__${expr.method}";
- Integer i = 0;
- while (i < this.methodRetNames.length()) {
- if (this.methodRetNames.get(i) as String == key) {
- return this.methodRetClasses.get(i) as String;
- }
- i = i + 1;
- }
- // Built-in method (String/List): assume method returns receiver type (common for chaining)
- return objCls;
- }
- return "";
- }
- return "";
- }
-
- private inferStaticCallReturn(className: String, methodName: String) : String {
- String key = "${className}__${methodName}";
- Integer i = 0;
- while (i < this.methodRetNames.length()) {
- if (this.methodRetNames.get(i) as String == key) {
- return this.methodRetClasses.get(i) as String;
- }
- i = i + 1;
- }
- // Known built-in returning String
- if (className == "Env" && methodName == "platform") { return "String"; }
- return "";
- }
-
- private isIntegerExpr(expr: Expr) : Boolean {
- if (expr.kind == ExprKind.INT_LIT) { return true; }
- if (expr.kind == ExprKind.BOOL_LIT) { return true; }
- if (expr.kind == ExprKind.BINOP) {
- Integer bop = expr.op;
- if (bop == BinaryOp.ADD || bop == BinaryOp.SUB ||
- bop == BinaryOp.MUL || bop == BinaryOp.DIV ||
- bop == BinaryOp.MOD || bop == BinaryOp.EQ ||
- bop == BinaryOp.NE || bop == BinaryOp.LT ||
- bop == BinaryOp.LE || bop == BinaryOp.GT ||
- bop == BinaryOp.GE) { return true; }
- }
- if (expr.kind == ExprKind.METHOD) {
- String m = expr.method;
- if (m == "length" || m == "size" || m == "indexOf" ||
- m == "charCodeAt" || m == "compareTo") { return true; }
- }
- return false;
- }
-
- private isStringExpr(expr: Expr) : Boolean {
- if (expr.kind == ExprKind.STR_LIT) { return true; }
- if (expr.kind == ExprKind.STR_INTERP) { return true; }
- if (expr.kind == ExprKind.IDENT) {
- String cls = this.varClassOf(expr.strVal);
- return cls == "String";
- }
- if (expr.kind == ExprKind.FIELD) {
- String objCls = this.inferClass(expr.object);
- Integer clsIdx = this.classIdxOf(objCls);
- if (clsIdx >= 0) {
- Integer fldIdx = this.fieldIdxOf(clsIdx, expr.field);
- if (fldIdx >= 0) {
- Integer start = this.classFldStarts.get(clsIdx) as Integer;
- String fldCls = this.allFieldClasses.get(start + fldIdx - 1) as String;
- return fldCls == "String";
- }
- }
- return false;
- }
- if (expr.kind == ExprKind.METHOD) {
- // Check method return type via class metadata first
- if (this.inferClass(expr) == "String") { return true; }
- // Check static method call on known class (e.g., Functions.greet())
- if (expr.object.kind == ExprKind.IDENT) {
- String key = "${expr.object.strVal}__${expr.method}";
- Integer ki = 0;
- while (ki < this.methodRetNames.length()) {
- if (this.methodRetNames.get(ki) as String == key) {
- if (this.methodRetClasses.get(ki) as String == "String") { return true; }
- }
- ki = ki + 1;
- }
- }
- // Fallback: hardcoded built-in string-returning methods
- String m = expr.method;
- if (m == "concat" || m == "substring" || m == "toString" ||
- m == "toUpperCase" || m == "toLowerCase" || m == "trim" ||
- m == "charAt") { return true; }
- return false;
- }
- if (expr.kind == ExprKind.STATIC_CALL) {
- return this.inferStaticCallReturn(expr.class_, expr.method) == "String";
- }
- if (expr.kind == ExprKind.CTOR) { return expr.class_ == "String"; }
- if (expr.kind == ExprKind.CAST) {
- if (expr.castTy.kind == TypeKind.STR) { return true; }
- if (expr.castTy.kind == TypeKind.NAMED) { return expr.castTy.name == "String"; }
- return false;
- }
- return false;
- }
-
- // ===== Core IR helpers =====
-
- private beginFn(name: String, retTy: Integer) {
- this.irFns.append(IRFunction(name, retTy));
- }
-
- private lastFn() : IRFunction {
- Integer last = this.irFns.length() - 1;
- return this.irFns.get(last) as IRFunction;
- }
-
- private addParamToLast(name: String, ty: Integer) {
- IRFunction f = this.lastFn();
- f.addParam(name, ty);
- }
-
- private newReg() : String {
- Integer n = this.regCnt;
- this.regCnt = this.regCnt + 1;
- return "t${n}";
- }
-
- private newLabel(prefix: String) : String {
- Integer n = this.labelCnt;
- this.labelCnt = this.labelCnt + 1;
- return "${prefix}${n}";
- }
-
- private internStr(content: String) : String {
- Integer i = 0;
- while (i < this.strConts.length()) {
- String c = this.strConts.get(i) as String;
- if (c == content) { return this.strNames.get(i) as String; }
- i = i + 1;
- }
- String name = "__str${this.strCnt}";
- this.strCnt = this.strCnt + 1;
- this.strNames.append(name);
- this.strConts.append(content);
- return name;
- }
-
- private emit(instr: IRInstr) {
- IRFunction f = this.lastFn();
- f.instrs.append(instr);
- }
-
- private varLookup(name: String) : String {
- Integer i = 0;
- while (i < this.varNames.length()) {
- String n = this.varNames.get(i) as String;
- if (n == name) { return this.varRegs.get(i) as String; }
- i = i + 1;
- }
- return "";
- }
-
- private varDefine(name: String, reg: String) {
- Integer i = 0;
- while (i < this.varNames.length()) {
- String n = this.varNames.get(i) as String;
- if (n == name) { this.varRegs.set(i, reg); return; }
- i = i + 1;
- }
- this.varNames.append(name);
- this.varRegs.append(reg);
- this.varClassNames.append("");
- this.trackScopedVar(name);
- }
-
- private resetFnContext() {
- this.varNames = List();
- this.varRegs = List();
- this.varClassNames = List();
- this.scopeStack = List();
- this.regCnt = 0;
- this.curClass = "";
- this.thisReg = "";
- }
-
- // ===== Scope management =====
-
- private pushScope() {
- this.scopeStack.append(List());
- }
-
- private trackScopedVar(name: String) {
- Integer n = this.scopeStack.length();
- if (n > 0) {
- List top = this.scopeStack.get(n - 1) as List;
- top.append(name);
- }
- }
-
- private popScope() {
- Integer n = this.scopeStack.length();
- if (n == 0) { return; }
- List vars = this.scopeStack.get(n - 1) as List;
- Integer i = vars.length();
- while (i > 0) {
- i = i - 1;
- String vn = vars.get(i) as String;
- // TODO: replace magic-string "__this" with ownership metadata model
- if (vn == "__this") { continue; }
- String vc = this.varClassOf(vn);
- Integer vci = this.classIdxOf(vc);
- if (vci >= 0) {
- String vr = this.varLookup(vn);
- if (vr != "") {
- this.emitRelease(IRValue.reg(vr, IRType.I64), vci);
- }
- }
- }
- this.scopeStack.removeAt(n - 1);
- }
-
- private emitUnwindToDepth(targetDepth: Integer) {
- Integer n = this.scopeStack.length();
- while (n > targetDepth) {
- this.popScope();
- n = this.scopeStack.length();
- }
- }
-
- // ===== Heap + field helpers =====
-
- private emitHeapAlloc(sizeVal: IRValue) : IRValue {
- if (this.linux) {
- // mmap(NULL, size, PROT_READ|PROT_WRITE=3, MAP_PRIVATE|MAP_ANONYMOUS=34, -1, 0)
- String allocReg = this.newReg();
- IRValue allocDst = IRValue.reg(allocReg, IRType.PTR);
- List mmapArgs = List();
- mmapArgs.append(IRValue.ofInt(0, IRType.I64));
- mmapArgs.append(sizeVal);
- mmapArgs.append(IRValue.ofInt(3, IRType.I64));
- mmapArgs.append(IRValue.ofInt(34, IRType.I64));
- mmapArgs.append(IRValue.ofInt(-1, IRType.I64));
- mmapArgs.append(IRValue.ofInt(0, IRType.I64));
- this.emit(IRInstr.syscall(allocDst, 9, mmapArgs));
- return allocDst;
- }
- String phReg = this.newReg();
- IRValue phDst = IRValue.reg(phReg, IRType.I64);
- List phArgs = List();
- this.emit(IRInstr.call(phDst, "__ext__GetProcessHeap", phArgs));
- String allocReg = this.newReg();
- IRValue allocDst = IRValue.reg(allocReg, IRType.PTR);
- List haArgs = List();
- haArgs.append(phDst);
- haArgs.append(IRValue.ofInt(0, IRType.I64));
- haArgs.append(sizeVal);
- this.emit(IRInstr.call(allocDst, "__ext__HeapAlloc", haArgs));
- return allocDst;
- }
-
- private emitHeapFree(objPtr: IRValue, clsIdx: Integer) {
- Integer sz = this.classSizeBytes(clsIdx);
- if (this.linux) {
- List munmapArgs = List();
- munmapArgs.append(objPtr);
- munmapArgs.append(IRValue.ofInt(sz, IRType.I64));
- String freeReg = this.newReg();
- IRValue freeDst = IRValue.reg(freeReg, IRType.VOID);
- this.emit(IRInstr.syscall(freeDst, 11, munmapArgs));
- } else {
- String phReg = this.newReg();
- IRValue phDst = IRValue.reg(phReg, IRType.I64);
- List phArgs = List();
- this.emit(IRInstr.call(phDst, "__ext__GetProcessHeap", phArgs));
- List hfArgs = List();
- hfArgs.append(phDst);
- hfArgs.append(IRValue.ofInt(0, IRType.I64));
- hfArgs.append(objPtr);
- String freeReg = this.newReg();
- IRValue freeDst = IRValue.reg(freeReg, IRType.VOID);
- this.emit(IRInstr.call(freeDst, "__ext__HeapFree", hfArgs));
- }
- }
-
- private emitFieldPtr(objVal: IRValue, offset: Integer) : IRValue {
- String pReg = this.newReg();
- IRValue pDst = IRValue.reg(pReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, pDst, objVal, IRValue.ofInt(offset, IRType.I64)));
- return pDst;
- }
-
- private emitFieldLoad(objVal: IRValue, offset: Integer) : IRValue {
- IRValue ptr = this.emitFieldPtr(objVal, offset);
- String vReg = this.newReg();
- IRValue vDst = IRValue.reg(vReg, IRType.I64);
- this.emit(IRInstr.load(vDst, ptr, IRType.I64));
- return vDst;
- }
-
- private emitFieldStore(objVal: IRValue, offset: Integer, val: IRValue) {
- IRValue ptr = this.emitFieldPtr(objVal, offset);
- this.emit(IRInstr.store(val, ptr, IRType.I64));
- }
-
- // ===== ARC retain helper =====
-
- private emitRetain(objVal: IRValue, clsIdx: Integer) {
- // Null guard: retain(null) is no-op (prevents SIGSEGV when field param is null)
- String nullSkipLbl = this.newLabel("ret_null_skip");
- this.emit(IRInstr.cmp(objVal, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, nullSkipLbl));
-
- // Inline retain: inc [obj + refCountOff]
- // Zero CALL — pure IR instructions to avoid S1 linear-scan multi-call bug
- IRValue curRef = this.emitFieldLoad(objVal, this.refCountOff(clsIdx));
- String nr = this.newReg();
- IRValue newRef = IRValue.reg(nr, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, newRef, curRef, IRValue.ofInt(1, IRType.I64)));
- this.emitFieldStore(objVal, this.refCountOff(clsIdx), newRef);
-
- this.emit(IRInstr.label(nullSkipLbl));
- }
-
- // ===== Cached teardown routine generation =====
-
- private needsTeardown(clsIdx: Integer) : Boolean {
- Integer currIdx = clsIdx;
- while (currIdx >= 0) {
- Integer start = this.classFldStarts.get(currIdx) as Integer;
- Integer count = this.classFldCounts.get(currIdx) as Integer;
- Integer fi = 0;
- while (fi < count) {
- String fldCls = this.allFieldClasses.get(start + fi) as String;
- if (this.classIdxOf(fldCls) >= 0) { return true; }
- fi = fi + 1;
- }
- currIdx = this.classParents.get(currIdx) as Integer;
- }
- return false;
- }
-
- private emitTeardownBody(clsIdx: Integer) {
- // obj parameter = first param (RDI on Linux, RCX on Windows)
- IRValue objParam = IRValue.reg("obj", IRType.PTR);
-
- Integer currIdx = clsIdx;
- while (currIdx >= 0) {
- Integer start = this.classFldStarts.get(currIdx) as Integer;
- Integer count = this.classFldCounts.get(currIdx) as Integer;
- Integer fi = count;
- while (fi > 0) {
- fi = fi - 1;
- String fldCls = this.allFieldClasses.get(start + fi) as String;
- Integer fldClsIdx = this.classIdxOf(fldCls);
- if (fldClsIdx >= 0) {
- // Load field value
- IRValue fldVal = this.emitFieldLoad(objParam, (fi + 1) * 8);
-
- // Null guard for field
- String fldSkipLbl = this.newLabel("td_fld_skip");
- this.emit(IRInstr.cmp(fldVal, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, fldSkipLbl));
-
- // Decrement field refcount
- IRValue fldRC = this.emitFieldLoad(fldVal, this.refCountOff(fldClsIdx));
- String nr = this.newReg();
- IRValue newRC = IRValue.reg(nr, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, newRC, fldRC, IRValue.ofInt(1, IRType.I64)));
- this.emitFieldStore(fldVal, this.refCountOff(fldClsIdx), newRC);
-
- // Conditional free: only when field refcount reaches 0
- this.emit(IRInstr.cmp(newRC, IRValue.ofInt(0, IRType.I64)));
- String freeSkipLbl = this.newLabel("td_free_skip");
- this.emit(IRInstr.branch(IROpcode.JNE, freeSkipLbl));
-
- // CALL child teardown (may be same class → recursive, safe via rc guard)
- String childTd = this.teardownLabels.get(fldClsIdx) as String;
- if (childTd != "") {
- List args = List();
- args.append(fldVal);
- String dr = this.newReg();
- this.emit(IRInstr.call(IRValue.reg(dr, IRType.I64), childTd, args));
- }
-
- // Heap free child
- this.emitHeapFree(fldVal, fldClsIdx);
- this.emit(IRInstr.label(freeSkipLbl));
- this.emit(IRInstr.label(fldSkipLbl));
- }
- }
- currIdx = this.classParents.get(currIdx) as Integer;
- }
-
- this.emit(IRInstr.retVoid());
- }
-
- private generateTeardownRoutines() {
- Integer i = 0;
- while (i < this.classNames.length()) {
- if (this.needsTeardown(i)) {
- String label = "__arimo_td_${i}";
- this.teardownLabels.set(i, label);
- this.beginFn(label, IRType.VOID);
- this.addParamToLast("obj", IRType.PTR);
- this.emitTeardownBody(i);
- }
- i = i + 1;
- }
- }
-
- // ===== ARC release helper =====
-
- private emitRelease(objVal: IRValue, clsIdx: Integer) {
- // Null guard: release(null) is no-op (prevents SIGSEGV on x=null → scope exit)
- String nullSkipLbl = this.newLabel("rel_null_skip");
- this.emit(IRInstr.cmp(objVal, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, nullSkipLbl));
-
- // Inline release: dec [obj + refCountOff]
- IRValue curRef = this.emitFieldLoad(objVal, this.refCountOff(clsIdx));
- String nr = this.newReg();
- IRValue newRef = IRValue.reg(nr, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, newRef, curRef, IRValue.ofInt(1, IRType.I64)));
- this.emitFieldStore(objVal, this.refCountOff(clsIdx), newRef);
-
- // Conditional free: only when refcount reaches 0
- this.emit(IRInstr.cmp(newRef, IRValue.ofInt(0, IRType.I64)));
- String freeSkipLbl = this.newLabel("rel_free_skip");
- this.emit(IRInstr.branch(IROpcode.JNE, freeSkipLbl));
-
- // Cached teardown routine: field teardown generated once per class,
- // called via runtime CALL (replaces exponential inline expansion).
- String tdLabel = this.teardownLabels.get(clsIdx) as String;
- if (tdLabel != "") {
- List tdArgs = List();
- tdArgs.append(objVal);
- String tdReg = this.newReg();
- this.emit(IRInstr.call(IRValue.reg(tdReg, IRType.I64), tdLabel, tdArgs));
- }
-
- this.emitHeapFree(objVal, clsIdx);
- this.emit(IRInstr.label(freeSkipLbl));
-
- this.emit(IRInstr.label(nullSkipLbl));
- }
-
- // ===== Condition lowering =====
-
- private lowerCond(expr: Expr) : Integer {
- if (expr.kind == ExprKind.BINOP) {
- Integer bop = expr.op;
- if (bop == BinaryOp.EQ || bop == BinaryOp.NE ||
- bop == BinaryOp.LT || bop == BinaryOp.LE ||
- bop == BinaryOp.GT || bop == BinaryOp.GE) {
- IRValue lv = this.lowerExpr(expr.left);
- IRValue rv = this.lowerExpr(expr.right);
- this.emit(IRInstr.cmp(lv, rv));
- if (bop == BinaryOp.EQ) { return IROpcode.JNE; }
- if (bop == BinaryOp.NE) { return IROpcode.JE; }
- if (bop == BinaryOp.LT) { return IROpcode.JGE; }
- if (bop == BinaryOp.LE) { return IROpcode.JG; }
- if (bop == BinaryOp.GT) { return IROpcode.JLE; }
- return IROpcode.JL;
- }
- }
- IRValue cv = this.lowerExpr(expr);
- this.emit(IRInstr.cmp(cv, IRValue.ofInt(0, IRType.I64)));
- return IROpcode.JE;
- }
-
- // ===== Statement lowering =====
-
- private needsReturnRetain(expr: Expr) : Boolean {
- if (expr.kind == ExprKind.IDENT) { return true; }
- if (expr.kind == ExprKind.FIELD) { return true; }
- // TODO: extend to INDEX/DEREF when indexed/deref field access is lowered
- return false;
- }
-
- private lowerStmt(stmt: Stmt) {
- if (stmt.kind == StmtKind.RETURN) {
- if (stmt.expr.kind != ExprKind.NULL_LIT) {
- IRValue rv = this.lowerExpr(stmt.expr);
- // ARC: retain ref-type return value before scope release
- if (this.needsReturnRetain(stmt.expr)) {
- String retCls = this.inferClass(stmt.expr);
- Integer retClsIdx = this.classIdxOf(retCls);
- if (retClsIdx >= 0) { this.emitRetain(rv, retClsIdx); }
- }
- // Full scope unwind
- this.emitUnwindToDepth(0);
- this.emit(IRInstr.ret(rv));
- } else {
- this.emitUnwindToDepth(0);
- this.emit(IRInstr.retVoid());
- }
- return;
- }
-
- if (stmt.kind == StmtKind.EXPR) {
- this.lowerExpr(stmt.expr);
- return;
- }
-
- if (stmt.kind == StmtKind.VAR_DECL) {
- String reg = this.newReg();
- IRValue dst = IRValue.reg(reg, IRType.I64);
- String varCls = this.tyToClass(stmt.ty);
- if (stmt.initExpr.kind != ExprKind.NULL_LIT) {
- IRValue src = this.lowerExpr(stmt.initExpr);
- // ARC: retain ref-type initializers (let c = a)
- if (stmt.initExpr.kind == ExprKind.IDENT) {
- String initCls = this.inferClass(stmt.initExpr);
- Integer initClsIdx = this.classIdxOf(initCls);
- if (initClsIdx >= 0) { this.emitRetain(src, initClsIdx); }
- }
- this.emit(IRInstr.mov(dst, src));
- if (varCls == "") { varCls = this.inferClass(stmt.initExpr); }
- } else {
- this.emit(IRInstr.mov(dst, IRValue.ofInt(0, IRType.I64)));
- }
- this.varDefine(stmt.name, reg);
- if (varCls != "") { this.varSetClass(stmt.name, varCls); }
- return;
- }
-
- if (stmt.kind == StmtKind.IF) {
- String endL = this.newLabel("if_end");
- String nextL = this.newLabel("if_else");
- Integer jmpOp = this.lowerCond(stmt.cond);
- this.emit(IRInstr.branch(jmpOp, nextL));
- this.lowerBody(stmt.thenBody);
- this.emit(IRInstr.jmp(endL));
- this.emit(IRInstr.label(nextL));
- Integer ei = 0;
- while (ei < stmt.elseIfs.length()) {
- ElseIfBranch eib = stmt.elseIfs.get(ei) as ElseIfBranch;
- String afterL = this.newLabel("if_else");
- Integer jopEI = this.lowerCond(eib.cond);
- this.emit(IRInstr.branch(jopEI, afterL));
- this.lowerBody(eib.body);
- this.emit(IRInstr.jmp(endL));
- this.emit(IRInstr.label(afterL));
- ei = ei + 1;
- }
- if (stmt.elseBody != null && stmt.elseBody.length() > 0) {
- this.lowerBody(stmt.elseBody);
- }
- this.emit(IRInstr.label(endL));
- return;
- }
-
- if (stmt.kind == StmtKind.WHILE) {
- String loopL = this.newLabel("while_loop");
- String breakL = this.newLabel("while_brk");
- String savedBreak = this.breakLbl;
- String savedCont = this.contLbl;
- Integer savedBreakDepth = this.breakDepth;
- Integer savedContDepth = this.contDepth;
- this.breakLbl = breakL;
- this.contLbl = loopL;
- this.breakDepth = this.scopeStack.length();
- this.contDepth = this.scopeStack.length();
- this.emit(IRInstr.label(loopL));
- Integer jmpOp = this.lowerCond(stmt.cond);
- this.emit(IRInstr.branch(jmpOp, breakL));
- this.lowerBody(stmt.body);
- this.emit(IRInstr.jmp(loopL));
- this.emit(IRInstr.label(breakL));
- this.breakLbl = savedBreak;
- this.contLbl = savedCont;
- this.breakDepth = savedBreakDepth;
- this.contDepth = savedContDepth;
- return;
- }
-
- if (stmt.kind == StmtKind.FOR) {
- if (stmt.forInit != null) { this.lowerStmt(stmt.forInit); }
- String loopL = this.newLabel("for_loop");
- String breakL = this.newLabel("for_brk");
- String contL = this.newLabel("for_upd");
- String savedBreak = this.breakLbl;
- String savedCont = this.contLbl;
- Integer savedBreakDepth = this.breakDepth;
- Integer savedContDepth = this.contDepth;
- this.breakLbl = breakL;
- this.contLbl = contL;
- this.breakDepth = this.scopeStack.length();
- this.contDepth = this.scopeStack.length();
- this.emit(IRInstr.label(loopL));
- if (stmt.forCond != null) {
- Integer jop = this.lowerCond(stmt.forCond);
- this.emit(IRInstr.branch(jop, breakL));
- }
- this.lowerBody(stmt.forBody);
- this.emit(IRInstr.label(contL));
- if (stmt.forStep != null) { this.lowerExpr(stmt.forStep); }
- this.emit(IRInstr.jmp(loopL));
- this.emit(IRInstr.label(breakL));
- this.breakLbl = savedBreak;
- this.contLbl = savedCont;
- this.breakDepth = savedBreakDepth;
- this.contDepth = savedContDepth;
- return;
- }
-
- if (stmt.kind == StmtKind.FOR_EACH) {
- IRValue iterVal = this.lowerExpr(stmt.iterable);
- String iReg = this.newReg();
- IRValue iDst = IRValue.reg(iReg, IRType.I64);
- this.emit(IRInstr.mov(iDst, IRValue.ofInt(0, IRType.I64)));
- this.varDefine("__fe_i_${iReg}", iReg);
- String loopL = this.newLabel("fe_loop");
- String breakL = this.newLabel("fe_brk");
- String savedBreak = this.breakLbl;
- String savedCont = this.contLbl;
- Integer savedBreakDepth = this.breakDepth;
- Integer savedContDepth = this.contDepth;
- this.breakLbl = breakL;
- this.contLbl = loopL;
- this.breakDepth = this.scopeStack.length();
- this.contDepth = this.scopeStack.length();
- this.emit(IRInstr.label(loopL));
- String lenReg = this.newReg();
- IRValue lenDst = IRValue.reg(lenReg, IRType.I64);
- List lenArgs = List();
- lenArgs.append(iterVal);
- this.emit(IRInstr.call(lenDst, "__arimo_list_length", lenArgs));
- this.emit(IRInstr.cmp(iDst, lenDst));
- this.emit(IRInstr.branch(IROpcode.JGE, breakL));
- String itemReg = this.newReg();
- IRValue itemDst = IRValue.reg(itemReg, IRType.I64);
- List getArgs = List();
- getArgs.append(iterVal);
- getArgs.append(iDst);
- this.emit(IRInstr.call(itemDst, "__arimo_list_get", getArgs));
- this.varDefine(stmt.iterName, itemReg);
- String iterCls = this.tyToClass(stmt.iterTy);
- if (iterCls != "") { this.varSetClass(stmt.iterName, iterCls); }
- this.lowerBody(stmt.body);
- this.emit(IRInstr.binop(IROpcode.ADD, iDst, iDst, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp(loopL));
- this.emit(IRInstr.label(breakL));
- this.breakLbl = savedBreak;
- this.contLbl = savedCont;
- this.breakDepth = savedBreakDepth;
- this.contDepth = savedContDepth;
- return;
- }
-
- if (stmt.kind == StmtKind.BREAK) {
- this.emitUnwindToDepth(this.breakDepth);
- this.emit(IRInstr.jmp(this.breakLbl));
- return;
- }
- if (stmt.kind == StmtKind.CONTINUE) {
- this.emitUnwindToDepth(this.contDepth);
- this.emit(IRInstr.jmp(this.contLbl));
- return;
- }
- if (stmt.kind == StmtKind.BLOCK) { this.pushScope(); this.lowerBody(stmt.body); this.popScope(); return; }
- if (stmt.kind == StmtKind.THROW) { this.lowerExpr(stmt.expr); return; }
- if (stmt.kind == StmtKind.TRY) { this.lowerBody(stmt.tryBody); return; }
- }
-
- private lowerBody(stmts: List) {
- Integer i = 0;
- while (i < stmts.length()) {
- Stmt s = stmts.get(i) as Stmt;
- this.lowerStmt(s);
- i = i + 1;
- }
- }
-
- // ===== Expression lowering =====
-
- private lowerExpr(expr: Expr) : IRValue {
- if (expr.kind == ExprKind.INT_LIT) { return IRValue.ofInt(expr.intVal, IRType.I64); }
- if (expr.kind == ExprKind.CHAR_LIT) { return IRValue.ofInt(expr.intVal, IRType.I64); }
-
- if (expr.kind == ExprKind.BOOL_LIT) {
- if (expr.boolVal) { return IRValue.ofInt(1, IRType.I64); }
- return IRValue.ofInt(0, IRType.I64);
- }
-
- if (expr.kind == ExprKind.NULL_LIT) { return IRValue.ofInt(0, IRType.I64); }
-
- if (expr.kind == ExprKind.STR_LIT) {
- String name = this.internStr(expr.strVal);
- return IRValue.global(name);
- }
-
- if (expr.kind == ExprKind.IDENT) {
- String reg = this.varLookup(expr.strVal);
- if (reg != "") { return IRValue.reg(reg, IRType.I64); }
- return IRValue.ofInt(0, IRType.I64);
- }
-
- if (expr.kind == ExprKind.THIS) {
- if (this.thisReg != "") { return IRValue.reg(this.thisReg, IRType.PTR); }
- return IRValue.ofInt(0, IRType.I64);
- }
-
- if (expr.kind == ExprKind.FIELD) {
- IRValue objVal = this.lowerExpr(expr.object);
- String objCls = this.inferClass(expr.object);
- Integer clsIdx = this.classIdxOf(objCls);
- if (clsIdx >= 0) {
- Integer fldIdx = this.fieldIdxOf(clsIdx, expr.field);
- if (fldIdx >= 0) { return this.emitFieldLoad(objVal, fldIdx * 8); }
- }
- return this.emitFieldLoad(objVal, 0);
- }
-
- if (expr.kind == ExprKind.NULL_SAFE) {
- if (expr.args == null || expr.args.length() == 0) {
- IRValue objVal = this.lowerExpr(expr.object);
- String objCls = this.inferClass(expr.object);
- Integer clsIdx = this.classIdxOf(objCls);
- if (clsIdx >= 0) {
- Integer fldIdx = this.fieldIdxOf(clsIdx, expr.field);
- if (fldIdx >= 0) { return this.emitFieldLoad(objVal, fldIdx * 8); }
- }
- return this.emitFieldLoad(objVal, 0);
- }
- return this.lowerMethodCall(expr.object, expr.field, expr.args);
- }
-
- if (expr.kind == ExprKind.CTOR) {
- return this.lowerCtor(expr.class_, expr.args);
- }
-
- if (expr.kind == ExprKind.STATIC_CALL) {
- return this.lowerStaticCall(expr.class_, expr.method, expr.args);
- }
-
- if (expr.kind == ExprKind.METHOD) {
- return this.lowerMethodCall(expr.object, expr.method, expr.args);
- }
-
- if (expr.kind == ExprKind.BINOP) {
- return this.lowerBinop(expr);
- }
-
- if (expr.kind == ExprKind.UNARY) {
- IRValue operand = this.lowerExpr(expr.operand);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- if (expr.op == 1) { this.emit(IRInstr.unop(IROpcode.NEG, dst, operand)); }
- else if (expr.op == 2) { this.emit(IRInstr.unop(IROpcode.NOT, dst, operand)); }
- else if (expr.op == 4 || expr.op == 5 || expr.op == 6 || expr.op == 7) {
- if (expr.operand.kind == ExprKind.IDENT) {
- String varName = expr.operand.strVal;
- String oldReg = this.varLookup(varName);
- if (oldReg != "") {
- IRValue inPlace = IRValue.reg(oldReg, IRType.I64);
- if (expr.op == 6 || expr.op == 7) {
- // POST: copy old value to dst, then update oldReg in-place
- this.emit(IRInstr.mov(dst, inPlace));
- if (expr.op == 6) { this.emit(IRInstr.binop(IROpcode.ADD, inPlace, inPlace, IRValue.ofInt(1, IRType.I64))); }
- else { this.emit(IRInstr.binop(IROpcode.SUB, inPlace, inPlace, IRValue.ofInt(1, IRType.I64))); }
- } else {
- // PRE: update oldReg in-place, return it (now holds new value)
- if (expr.op == 4) { this.emit(IRInstr.binop(IROpcode.ADD, inPlace, inPlace, IRValue.ofInt(1, IRType.I64))); }
- else { this.emit(IRInstr.binop(IROpcode.SUB, inPlace, inPlace, IRValue.ofInt(1, IRType.I64))); }
- return inPlace;
- }
- } else {
- this.emit(IRInstr.mov(dst, operand));
- }
- } else {
- this.emit(IRInstr.mov(dst, operand));
- }
- }
- else { this.emit(IRInstr.mov(dst, operand)); }
- return dst;
- }
-
- if (expr.kind == ExprKind.CAST) {
- IRValue src = this.lowerExpr(expr.operand);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- this.emit(IRInstr.mov(dst, src));
- return dst;
- }
-
- if (expr.kind == ExprKind.TERNARY) {
- String falseL = this.newLabel("tern_f");
- String endL = this.newLabel("tern_e");
- String resReg = this.newReg();
- IRValue resDst = IRValue.reg(resReg, IRType.I64);
- Integer jop = this.lowerCond(expr.cond);
- this.emit(IRInstr.branch(jop, falseL));
- IRValue tv = this.lowerExpr(expr.then_);
- this.emit(IRInstr.mov(resDst, tv));
- this.emit(IRInstr.jmp(endL));
- this.emit(IRInstr.label(falseL));
- IRValue fv = this.lowerExpr(expr.else_);
- this.emit(IRInstr.mov(resDst, fv));
- this.emit(IRInstr.label(endL));
- return resDst;
- }
-
- if (expr.kind == ExprKind.NULL_COALESCE) {
- String notNullL = this.newLabel("nn_ok");
- String endL = this.newLabel("nn_end");
- String resReg = this.newReg();
- IRValue resDst = IRValue.reg(resReg, IRType.I64);
- IRValue lv = this.lowerExpr(expr.left);
- this.emit(IRInstr.mov(resDst, lv));
- this.emit(IRInstr.cmp(lv, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JNE, notNullL));
- IRValue rv = this.lowerExpr(expr.right);
- this.emit(IRInstr.mov(resDst, rv));
- this.emit(IRInstr.jmp(endL));
- this.emit(IRInstr.label(notNullL));
- this.emit(IRInstr.label(endL));
- return resDst;
- }
-
- if (expr.kind == ExprKind.STR_INTERP) {
- String emptyName = this.internStr("");
- IRValue result = IRValue.global(emptyName);
- Integer pi = 0;
- while (pi < expr.parts.length()) {
- StrPart part = expr.parts.get(pi) as StrPart;
- IRValue partVal = IRValue.ofInt(0, IRType.I64);
- if (part.isLit) {
- String sn = this.internStr(part.lit);
- partVal = IRValue.global(sn);
- } else {
- IRValue rawVal = this.lowerExpr(part.expr);
- if (this.isIntegerExpr(part.expr)) {
- String toStrReg = this.newReg();
- IRValue toStrDst = IRValue.reg(toStrReg, IRType.PTR);
- List tsArgs = List();
- tsArgs.append(rawVal);
- this.emit(IRInstr.call(toStrDst, "__arimo_i64_to_str", tsArgs));
- partVal = toStrDst;
- } else {
- partVal = rawVal;
- }
- }
- String catReg = this.newReg();
- IRValue catDst = IRValue.reg(catReg, IRType.PTR);
- List catArgs = List();
- catArgs.append(result);
- catArgs.append(partVal);
- this.emit(IRInstr.call(catDst, "__arimo_strcat", catArgs));
- result = catDst;
- pi = pi + 1;
- }
- return result;
- }
-
- return IRValue.ofInt(0, IRType.I64);
- }
-
- // ===== Binary op lowering =====
-
- private lowerBinop(expr: Expr) : IRValue {
- Integer bop = expr.op;
-
- if (bop == BinaryOp.ASSIGN) {
- if (expr.left.kind == ExprKind.IDENT) {
- String vReg = this.varLookup(expr.left.strVal);
- if (vReg != "") {
- IRValue rv = this.lowerExpr(expr.right);
- // ARC: retain new value if ref-type variable
- if (expr.right.kind == ExprKind.IDENT) {
- String rhsCls = this.inferClass(expr.right);
- Integer rhsClsIdx = this.classIdxOf(rhsCls);
- if (rhsClsIdx >= 0) { this.emitRetain(rv, rhsClsIdx); }
- }
- // ARC: release old value before overwrite
- String oldCls = this.varClassOf(expr.left.strVal);
- Integer oldClsIdx = this.classIdxOf(oldCls);
- if (oldClsIdx >= 0) {
- this.emitRelease(IRValue.reg(vReg, IRType.I64), oldClsIdx);
- }
- IRValue dst = IRValue.reg(vReg, IRType.I64);
- this.emit(IRInstr.mov(dst, rv));
- return dst;
- }
- }
- if (expr.left.kind == ExprKind.FIELD) {
- String objCls = this.inferClass(expr.left.object);
- Integer clsIdx = this.classIdxOf(objCls);
- IRValue objVal = this.lowerExpr(expr.left.object);
- Integer fldOff = 0;
- Integer fldIdx = -1;
- if (clsIdx >= 0) {
- fldIdx = this.fieldIdxOf(clsIdx, expr.left.field);
- if (fldIdx >= 0) { fldOff = fldIdx * 8; }
- }
- IRValue rv2 = this.lowerExpr(expr.right);
- // ARC: retain new value if ref-type variable (mirrors IDENT case)
- if (expr.right.kind == ExprKind.IDENT) {
- String rhsCls = this.inferClass(expr.right);
- Integer rhsClsIdx = this.classIdxOf(rhsCls);
- if (rhsClsIdx >= 0) { this.emitRetain(rv2, rhsClsIdx); }
- }
- // ARC: release old field value before overwrite
- if (clsIdx >= 0 && fldIdx >= 0) {
- Integer start = this.classFldStarts.get(clsIdx) as Integer;
- String oldFldCls = this.allFieldClasses.get(start + fldIdx - 1) as String;
- Integer oldFldClsIdx = this.classIdxOf(oldFldCls);
- if (oldFldClsIdx >= 0) {
- IRValue oldVal = this.emitFieldLoad(objVal, fldOff);
- this.emitRelease(oldVal, oldFldClsIdx);
- }
- }
- this.emitFieldStore(objVal, fldOff, rv2);
- return IRValue.ofInt(0, IRType.I64);
- }
- IRValue rv3 = this.lowerExpr(expr.right);
- return rv3;
- }
-
- if (bop == BinaryOp.AND || bop == BinaryOp.OR) {
- IRValue lv = this.lowerExpr(expr.left);
- IRValue rv = this.lowerExpr(expr.right);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- if (bop == BinaryOp.AND) { this.emit(IRInstr.binop(IROpcode.AND, dst, lv, rv)); }
- else { this.emit(IRInstr.binop(IROpcode.OR, dst, lv, rv)); }
- return dst;
- }
-
- if (bop == BinaryOp.EQ || bop == BinaryOp.NE ||
- bop == BinaryOp.LT || bop == BinaryOp.LE ||
- bop == BinaryOp.GT || bop == BinaryOp.GE) {
- IRValue lv = this.lowerExpr(expr.left);
- IRValue rv = this.lowerExpr(expr.right);
- String trueL = this.newLabel("cmp_t");
- String endL = this.newLabel("cmp_e");
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- this.emit(IRInstr.cmp(lv, rv));
- Integer trueJmp = IROpcode.JE;
- if (bop == BinaryOp.EQ) { trueJmp = IROpcode.JE; }
- if (bop == BinaryOp.NE) { trueJmp = IROpcode.JNE; }
- if (bop == BinaryOp.LT) { trueJmp = IROpcode.JL; }
- if (bop == BinaryOp.LE) { trueJmp = IROpcode.JLE; }
- if (bop == BinaryOp.GT) { trueJmp = IROpcode.JG; }
- if (bop == BinaryOp.GE) { trueJmp = IROpcode.JGE; }
- this.emit(IRInstr.branch(trueJmp, trueL));
- this.emit(IRInstr.mov(dst, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.jmp(endL));
- this.emit(IRInstr.label(trueL));
- this.emit(IRInstr.mov(dst, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.label(endL));
- return dst;
- }
-
- Integer irop = IROpcode.ADD;
- if (bop == BinaryOp.ADD) { irop = IROpcode.ADD; }
- if (bop == BinaryOp.SUB) { irop = IROpcode.SUB; }
- if (bop == BinaryOp.MUL) { irop = IROpcode.MUL; }
- if (bop == BinaryOp.DIV) { irop = IROpcode.DIV; }
- if (bop == BinaryOp.MOD) { irop = IROpcode.MOD; }
- if (bop == BinaryOp.BITAND) { irop = IROpcode.AND; }
- if (bop == BinaryOp.BITOR) { irop = IROpcode.OR; }
- if (bop == BinaryOp.XOR) { irop = IROpcode.XOR; }
- if (bop == BinaryOp.SHL) { irop = IROpcode.SHL; }
- if (bop == BinaryOp.SHR) { irop = IROpcode.SHR; }
-
- IRValue lv2 = this.lowerExpr(expr.left);
- IRValue rv2 = this.lowerExpr(expr.right);
- String dr2 = this.newReg();
- IRValue dst2 = IRValue.reg(dr2, IRType.I64);
- this.emit(IRInstr.binop(irop, dst2, lv2, rv2));
- return dst2;
- }
-
- // ===== Static call lowering =====
-
- private lowerStaticCall(class_: String, method: String, args: List) : IRValue {
- if (class_ == "IO" && method == "println") {
- Expr argExpr = args.get(0) as Expr;
- Boolean isStr = this.isStringExpr(argExpr);
- if (argExpr.kind == ExprKind.METHOD) {
- if (argExpr.object.kind == ExprKind.IDENT && argExpr.object.strVal == "Env") { isStr = true; }
- }
- if (argExpr.kind == ExprKind.STATIC_CALL) {
- if (argExpr.class_ == "Env") { isStr = true; }
- }
- if (isStr) {
- IRValue strArg = this.lowerExpr(argExpr);
- List callArgs = List();
- callArgs.append(strArg);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.VOID);
- this.emit(IRInstr.call(dst, "__arimo_println", callArgs));
- } else {
- IRValue rawVal = this.lowerExpr(argExpr);
- List intArgs = List();
- intArgs.append(rawVal);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.VOID);
- this.emit(IRInstr.call(dst, "__arimo_println_int", intArgs));
- }
- return IRValue.none();
- }
- if (class_ == "IO" && method == "print") {
- Expr argExpr2 = args.get(0) as Expr;
- Boolean isStr2 = this.isStringExpr(argExpr2);
- if (argExpr2.kind == ExprKind.METHOD) {
- if (argExpr2.object.kind == ExprKind.IDENT && argExpr2.object.strVal == "Env") { isStr2 = true; }
- }
- if (isStr2) {
- IRValue strArg2 = this.lowerExpr(argExpr2);
- List callArgs2 = List();
- callArgs2.append(strArg2);
- String dr2 = this.newReg();
- IRValue dst2 = IRValue.reg(dr2, IRType.VOID);
- this.emit(IRInstr.call(dst2, "__arimo_print", callArgs2));
- } else {
- IRValue rawVal2 = this.lowerExpr(argExpr2);
- List intArgs2 = List();
- intArgs2.append(rawVal2);
- String dr2 = this.newReg();
- IRValue dst2 = IRValue.reg(dr2, IRType.VOID);
- this.emit(IRInstr.call(dst2, "__arimo_print_int", intArgs2));
- }
- return IRValue.none();
- }
-
- // Env built-ins
- if (class_ == "Env" && method == "platform") {
- String platform = "linux";
- if (!this.linux) { platform = "windows"; }
- return IRValue.global(this.internStr(platform));
- }
- if (class_ == "Env" && method == "exit") {
- Integer code = 0;
- if (args.length() > 0) {
- Expr e = args.get(0) as Expr;
- if (e.kind == ExprKind.INT_LIT) { code = e.intVal; }
- }
- if (this.linux) {
- List scArgs = List();
- scArgs.append(IRValue.ofInt(code, IRType.I64));
- this.emit(IRInstr.syscall(IRValue.none(), 60, scArgs));
- } else {
- List ea = List();
- ea.append(IRValue.ofInt(code, IRType.I64));
- String er = this.newReg();
- this.emit(IRInstr.call(IRValue.reg(er, IRType.VOID), "__ext__ExitProcess", ea));
- }
- return IRValue.none();
- }
- if (class_ == "Env" && method == "args") {
- // Stub: return empty List. Full argc/argv startup ABI → V1.1.
- List la = List();
- String lr = this.newReg();
- this.emit(IRInstr.call(IRValue.reg(lr, IRType.PTR), "__arimo_list_new", la));
- return IRValue.reg(lr, IRType.PTR);
- }
- if (class_ == "Env" && method == "exePath") {
- // Stub: return empty string. Full exePath → V1.1.
- return IRValue.global(this.internStr(""));
- }
- // Generic static call fallback
- String fnName = "${class_}__${method}";
- List callArgsFn = List();
- Integer ai = 0;
- while (ai < args.length()) {
- Expr arg = args.get(ai) as Expr;
- callArgsFn.append(this.lowerExpr(arg));
- ai = ai + 1;
- }
- String dr3 = this.newReg();
- IRValue dst3 = IRValue.reg(dr3, IRType.I64);
- this.emit(IRInstr.call(dst3, fnName, callArgsFn));
- return dst3;
- }
-
- // ===== Constructor lowering =====
-
- private lowerLinuxLibcCall(name: String, args: List) : IRValue {
- // fopen(path, mode) → open(path, flags, 0o666=438) syscall
- if (name == "fopen") {
- IRValue pathVal = this.lowerExpr(args.get(0) as Expr);
- IRValue modeVal = this.lowerExpr(args.get(1) as Expr);
- String mbyteR = this.newReg();
- IRValue mbyte = IRValue.reg(mbyteR, IRType.I64);
- this.emit(IRInstr.load(mbyte, modeVal, IRType.I8));
- String flagsR = this.newReg();
- IRValue flags = IRValue.reg(flagsR, IRType.I64);
- String notRLbl = this.newLabel("fo_nr");
- String notALbl = this.newLabel("fo_na");
- String openLbl = this.newLabel("fo_op");
- this.emit(IRInstr.cmp(mbyte, IRValue.ofInt(114, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JNE, notRLbl));
- this.emit(IRInstr.mov(flags, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.jmp(openLbl));
- this.emit(IRInstr.label(notRLbl));
- this.emit(IRInstr.cmp(mbyte, IRValue.ofInt(97, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JNE, notALbl));
- this.emit(IRInstr.mov(flags, IRValue.ofInt(1089, IRType.I64)));
- this.emit(IRInstr.jmp(openLbl));
- this.emit(IRInstr.label(notALbl));
- this.emit(IRInstr.mov(flags, IRValue.ofInt(577, IRType.I64)));
- this.emit(IRInstr.label(openLbl));
- String fdR = this.newReg();
- IRValue fd = IRValue.reg(fdR, IRType.I64);
- List openArgs = List();
- openArgs.append(pathVal);
- openArgs.append(flags);
- openArgs.append(IRValue.ofInt(438, IRType.I64));
- this.emit(IRInstr.syscall(fd, 2, openArgs));
- String retR = this.newReg();
- IRValue ret = IRValue.reg(retR, IRType.I64);
- String errLbl = this.newLabel("fo_er");
- String okLbl = this.newLabel("fo_ok");
- this.emit(IRInstr.cmp(fd, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JL, errLbl));
- this.emit(IRInstr.mov(ret, fd));
- this.emit(IRInstr.jmp(okLbl));
- this.emit(IRInstr.label(errLbl));
- this.emit(IRInstr.mov(ret, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.label(okLbl));
- return ret;
- }
- // fread(buf, size, n, fd) → read(fd, buf, size*n) syscall 0
- if (name == "fread") {
- IRValue bufVal = this.lowerExpr(args.get(0) as Expr);
- IRValue sizeVal = this.lowerExpr(args.get(1) as Expr);
- IRValue nVal = this.lowerExpr(args.get(2) as Expr);
- IRValue fdVal = this.lowerExpr(args.get(3) as Expr);
- String totR = this.newReg();
- IRValue total = IRValue.reg(totR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MUL, total, sizeVal, nVal));
- String resR = this.newReg();
- IRValue result = IRValue.reg(resR, IRType.I64);
- List rdArgs = List();
- rdArgs.append(fdVal);
- rdArgs.append(bufVal);
- rdArgs.append(total);
- this.emit(IRInstr.syscall(result, 0, rdArgs));
- return result;
- }
- // fwrite(buf, size, n, fd) → write(fd, buf, size*n) syscall 1
- if (name == "fwrite") {
- IRValue bufVal = this.lowerExpr(args.get(0) as Expr);
- IRValue sizeVal = this.lowerExpr(args.get(1) as Expr);
- IRValue nVal = this.lowerExpr(args.get(2) as Expr);
- IRValue fdVal = this.lowerExpr(args.get(3) as Expr);
- String totR = this.newReg();
- IRValue total = IRValue.reg(totR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MUL, total, sizeVal, nVal));
- String resR = this.newReg();
- IRValue result = IRValue.reg(resR, IRType.I64);
- List wrArgs = List();
- wrArgs.append(fdVal);
- wrArgs.append(bufVal);
- wrArgs.append(total);
- this.emit(IRInstr.syscall(result, 1, wrArgs));
- return result;
- }
- // fclose(fd) → close(fd) syscall 3
- if (name == "fclose") {
- IRValue fdVal = this.lowerExpr(args.get(0) as Expr);
- String clR = this.newReg();
- IRValue clDst = IRValue.reg(clR, IRType.I64);
- List clArgs = List();
- clArgs.append(fdVal);
- this.emit(IRInstr.syscall(clDst, 3, clArgs));
- String zR = this.newReg();
- IRValue zero = IRValue.reg(zR, IRType.I64);
- this.emit(IRInstr.mov(zero, IRValue.ofInt(0, IRType.I64)));
- return zero;
- }
- // fseek(fd, offset, whence) → lseek(fd, offset, whence) syscall 8
- if (name == "fseek") {
- IRValue fdVal = this.lowerExpr(args.get(0) as Expr);
- IRValue offsetVal = this.lowerExpr(args.get(1) as Expr);
- IRValue whenceVal = this.lowerExpr(args.get(2) as Expr);
- String skR = this.newReg();
- IRValue skDst = IRValue.reg(skR, IRType.I64);
- List skArgs = List();
- skArgs.append(fdVal);
- skArgs.append(offsetVal);
- skArgs.append(whenceVal);
- this.emit(IRInstr.syscall(skDst, 8, skArgs));
- String zR = this.newReg();
- IRValue zero = IRValue.reg(zR, IRType.I64);
- this.emit(IRInstr.mov(zero, IRValue.ofInt(0, IRType.I64)));
- return zero;
- }
- // ftell(fd) → lseek(fd, 0, SEEK_CUR=1) syscall 8
- if (name == "ftell") {
- IRValue fdVal = this.lowerExpr(args.get(0) as Expr);
- String posR = this.newReg();
- IRValue pos = IRValue.reg(posR, IRType.I64);
- List ftArgs = List();
- ftArgs.append(fdVal);
- ftArgs.append(IRValue.ofInt(0, IRType.I64));
- ftArgs.append(IRValue.ofInt(1, IRType.I64));
- this.emit(IRInstr.syscall(pos, 8, ftArgs));
- return pos;
- }
- // remove(path) → unlink(path) syscall 87
- if (name == "remove") {
- IRValue pathVal = this.lowerExpr(args.get(0) as Expr);
- String rmR = this.newReg();
- IRValue rmDst = IRValue.reg(rmR, IRType.I64);
- List rmArgs = List();
- rmArgs.append(pathVal);
- this.emit(IRInstr.syscall(rmDst, 87, rmArgs));
- return rmDst;
- }
- // calloc(n, size) → mmap(NULL, n*size, PROT_RW=3, MAP_PRIVATE|ANON=34, -1, 0) syscall 9
- if (name == "calloc") {
- IRValue nVal = this.lowerExpr(args.get(0) as Expr);
- IRValue sizeVal = this.lowerExpr(args.get(1) as Expr);
- String totR = this.newReg();
- IRValue total = IRValue.reg(totR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MUL, total, nVal, sizeVal));
- String ptrR = this.newReg();
- IRValue ptr = IRValue.reg(ptrR, IRType.PTR);
- List mmArgs = List();
- mmArgs.append(IRValue.ofInt(0, IRType.I64));
- mmArgs.append(total);
- mmArgs.append(IRValue.ofInt(3, IRType.I64));
- mmArgs.append(IRValue.ofInt(34, IRType.I64));
- mmArgs.append(IRValue.ofInt(-1, IRType.I64));
- mmArgs.append(IRValue.ofInt(0, IRType.I64));
- this.emit(IRInstr.syscall(ptr, 9, mmArgs));
- return ptr;
- }
- // fputc(c, fd) → stack-alloc 1 byte, store c, write(fd, buf, 1) syscall 1
- if (name == "fputc") {
- IRValue cVal = this.lowerExpr(args.get(0) as Expr);
- IRValue fdVal = this.lowerExpr(args.get(1) as Expr);
- String bufR = this.newReg();
- IRValue buf = IRValue.reg(bufR, IRType.PTR);
- this.emit(IRInstr.alloc(buf, 1));
- this.emit(IRInstr.store(cVal, buf, IRType.I8));
- String wR = this.newReg();
- IRValue wDst = IRValue.reg(wR, IRType.I64);
- List wArgs = List();
- wArgs.append(fdVal);
- wArgs.append(buf);
- wArgs.append(IRValue.ofInt(1, IRType.I64));
- this.emit(IRInstr.syscall(wDst, 1, wArgs));
- return cVal;
- }
- return IRValue.none();
- }
-
- private lowerCtor(class_: String, args: List) : IRValue {
- if (class_ == "List") {
- String lr = this.newReg();
- IRValue lDst = IRValue.reg(lr, IRType.PTR);
- List lArgs = List();
- this.emit(IRInstr.call(lDst, "__arimo_list_new", lArgs));
- return lDst;
- }
- if (this.linux) {
- IRValue libcRes = this.lowerLinuxLibcCall(class_, args);
- if (!libcRes.isNone()) { return libcRes; }
- }
- Integer clsIdx = this.classIdxOf(class_);
- Integer sz = 16;
- if (clsIdx >= 0) { sz = this.classSizeBytes(clsIdx); }
- IRValue objPtr = this.emitHeapAlloc(IRValue.ofInt(sz, IRType.I64));
- if (clsIdx >= 0) {
- this.emitFieldStore(objPtr, this.refCountOff(clsIdx), IRValue.ofInt(1, IRType.I64));
- }
- List initArgs = List();
- initArgs.append(objPtr);
- Integer ai = 0;
- while (ai < args.length()) {
- Expr arg = args.get(ai) as Expr;
- initArgs.append(this.lowerExpr(arg));
- ai = ai + 1;
- }
- String initRes = this.newReg();
- IRValue initDst = IRValue.reg(initRes, IRType.VOID);
- this.emit(IRInstr.call(initDst, "${class_}__init", initArgs));
- return objPtr;
- }
-
- // ===== Method call lowering =====
-
- private lowerMethodCall(obj: Expr, method: String, args: List) : IRValue {
- if (obj.kind == ExprKind.IDENT) {
- if (obj.strVal == "IO") {
- return this.lowerStaticCall("IO", method, args);
- }
- if (obj.strVal == "Env") {
- return this.lowerStaticCall("Env", method, args);
- }
- // Static method call on known class: ClassName.method()
- if (this.classIdxOf(obj.strVal) >= 0) {
- return this.lowerStaticCall(obj.strVal, method, args);
- }
- }
-
- IRValue objVal = this.lowerExpr(obj);
- String objCls = this.inferClass(obj);
-
- // List methods
- if (objCls == "List") {
- return this.lowerListMethod(objVal, method, args);
- }
-
- // toString() handler — must go before String/List dispatch to avoid
- // lowerStringMethod/lowerListMethod fallback generating __str_toString
- // which is never defined.
- if (method == "toString") {
- if (objCls == "String" || this.isStringExpr(obj)) { return IRValue.reg(objVal.name, objVal.ty); }
- if (objCls == "" || objCls == "Integer") {
- String tsReg = this.newReg();
- IRValue tsDst = IRValue.reg(tsReg, IRType.PTR);
- List tsArgs = List();
- tsArgs.append(objVal);
- this.emit(IRInstr.call(tsDst, "__arimo_i64_to_str", tsArgs));
- return tsDst;
- }
- // Unknown type: fall through
- }
-
- // String methods
- if (objCls == "String") {
- return this.lowerStringMethod(objVal, method, args);
- }
-
- // length() on unknown type: prefer list if we can't tell
- if (method == "length") {
- if (objCls == "") {
- String lr = this.newReg();
- IRValue lDst = IRValue.reg(lr, IRType.I64);
- List lArgs = List();
- lArgs.append(objVal);
- this.emit(IRInstr.call(lDst, "__arimo_strlen", lArgs));
- return lDst;
- }
- }
- // String method heuristic: call on unknown obj with string-like method names
- if (method == "compareTo" || method == "concat" || method == "startsWith" ||
- method == "endsWith" || method == "substring" || method == "indexOf" ||
- method == "charCodeAt"|| method == "charAt" || method == "isEmpty" ||
- method == "equals" || method == "toUpperCase"|| method == "toLowerCase") {
- return this.lowerStringMethod(objVal, method, args);
- }
-
- // User class method dispatch
- if (objCls != "") {
- List callArgsCls = List();
- callArgsCls.append(objVal);
- Integer ai = 0;
- while (ai < args.length()) {
- Expr arg = args.get(ai) as Expr;
- callArgsCls.append(this.lowerExpr(arg));
- ai = ai + 1;
- }
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- this.emit(IRInstr.call(dst, "${objCls}__${method}", callArgsCls));
- return dst;
- }
-
- // Fallback
- List fallbackArgs = List();
- fallbackArgs.append(objVal);
- Integer ai2 = 0;
- while (ai2 < args.length()) {
- Expr arg2 = args.get(ai2) as Expr;
- fallbackArgs.append(this.lowerExpr(arg2));
- ai2 = ai2 + 1;
- }
- String dr4 = this.newReg();
- IRValue dst4 = IRValue.reg(dr4, IRType.I64);
- this.emit(IRInstr.call(dst4, "__method_${method}", fallbackArgs));
- return dst4;
- }
-
- private lowerListMethod(objVal: IRValue, method: String, args: List) : IRValue {
- if (method == "length" || method == "size") {
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- List a = List();
- a.append(objVal);
- this.emit(IRInstr.call(dst, "__arimo_list_length", a));
- return dst;
- }
- if (method == "append" || method == "add") {
- IRValue val = this.lowerExpr(args.get(0) as Expr);
- List a = List();
- a.append(objVal);
- a.append(val);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.VOID);
- this.emit(IRInstr.call(dst, "__arimo_list_append", a));
- return IRValue.none();
- }
- if (method == "get") {
- IRValue idx = this.lowerExpr(args.get(0) as Expr);
- List a = List();
- a.append(objVal);
- a.append(idx);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- this.emit(IRInstr.call(dst, "__arimo_list_get", a));
- return dst;
- }
- if (method == "set") {
- IRValue idx = this.lowerExpr(args.get(0) as Expr);
- IRValue val = this.lowerExpr(args.get(1) as Expr);
- List a = List();
- a.append(objVal);
- a.append(idx);
- a.append(val);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.VOID);
- this.emit(IRInstr.call(dst, "__arimo_list_set", a));
- return IRValue.none();
- }
- if (method == "removeAt") {
- IRValue idx = this.lowerExpr(args.get(0) as Expr);
- List a = List();
- a.append(objVal);
- a.append(idx);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.VOID);
- this.emit(IRInstr.call(dst, "__arimo_list_remove_at", a));
- return IRValue.none();
- }
- // Fallback for other list methods
- String dr5 = this.newReg();
- IRValue dst5 = IRValue.reg(dr5, IRType.I64);
- List a5 = List();
- a5.append(objVal);
- Integer xi = 0;
- while (xi < args.length()) {
- a5.append(this.lowerExpr(args.get(xi) as Expr));
- xi = xi + 1;
- }
- this.emit(IRInstr.call(dst5, "__list_${method}", a5));
- return dst5;
- }
-
- private lowerStringMethod(objVal: IRValue, method: String, args: List) : IRValue {
- if (method == "length") {
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- List a = List();
- a.append(objVal);
- this.emit(IRInstr.call(dst, "__arimo_strlen", a));
- return dst;
- }
- if (method == "compareTo" || method == "equals") {
- IRValue other = this.lowerExpr(args.get(0) as Expr);
- List a = List();
- a.append(objVal);
- a.append(other);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- this.emit(IRInstr.call(dst, "__arimo_strcmp", a));
- return dst;
- }
- if (method == "concat") {
- IRValue other = this.lowerExpr(args.get(0) as Expr);
- List a = List();
- a.append(objVal);
- a.append(other);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.PTR);
- this.emit(IRInstr.call(dst, "__arimo_strcat", a));
- return dst;
- }
- if (method == "startsWith") {
- IRValue prefix = this.lowerExpr(args.get(0) as Expr);
- List a = List();
- a.append(objVal);
- a.append(prefix);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- this.emit(IRInstr.call(dst, "__arimo_startswith", a));
- return dst;
- }
- if (method == "endsWith") {
- IRValue suffix = this.lowerExpr(args.get(0) as Expr);
- List a = List();
- a.append(objVal);
- a.append(suffix);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- this.emit(IRInstr.call(dst, "__arimo_endswith", a));
- return dst;
- }
- if (method == "substring") {
- IRValue from = this.lowerExpr(args.get(0) as Expr);
- IRValue to = this.lowerExpr(args.get(1) as Expr);
- List a = List();
- a.append(objVal);
- a.append(from);
- a.append(to);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.PTR);
- this.emit(IRInstr.call(dst, "__arimo_substr", a));
- return dst;
- }
- if (method == "indexOf") {
- IRValue sub = this.lowerExpr(args.get(0) as Expr);
- IRValue from = IRValue.ofInt(0, IRType.I64);
- if (args.length() > 1) { from = this.lowerExpr(args.get(1) as Expr); }
- List a = List();
- a.append(objVal);
- a.append(sub);
- a.append(from);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- this.emit(IRInstr.call(dst, "__arimo_indexof_from", a));
- return dst;
- }
- if (method == "charCodeAt") {
- IRValue idx = this.lowerExpr(args.get(0) as Expr);
- List a = List();
- a.append(objVal);
- a.append(idx);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- this.emit(IRInstr.call(dst, "__arimo_charcodeat", a));
- return dst;
- }
- if (method == "charAt") {
- IRValue idx = this.lowerExpr(args.get(0) as Expr);
- List a = List();
- a.append(objVal);
- a.append(idx);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.PTR);
- this.emit(IRInstr.call(dst, "__arimo_charat", a));
- return dst;
- }
- if (method == "isEmpty") {
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.I64);
- List a = List();
- a.append(objVal);
- this.emit(IRInstr.call(dst, "__arimo_strlen", a));
- String eqL = this.newLabel("isemp_eq");
- String endL = this.newLabel("isemp_end");
- String res = this.newReg();
- IRValue resDst = IRValue.reg(res, IRType.I64);
- this.emit(IRInstr.cmp(dst, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, eqL));
- this.emit(IRInstr.mov(resDst, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.jmp(endL));
- this.emit(IRInstr.label(eqL));
- this.emit(IRInstr.mov(resDst, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.label(endL));
- return resDst;
- }
- // Fallback
- List fa = List();
- fa.append(objVal);
- Integer xi = 0;
- while (xi < args.length()) {
- fa.append(this.lowerExpr(args.get(xi) as Expr));
- xi = xi + 1;
- }
- String fdr = this.newReg();
- IRValue fdst = IRValue.reg(fdr, IRType.I64);
- this.emit(IRInstr.call(fdst, "__str_${method}", fa));
- return fdst;
- }
-
- // ===== Method / constructor body lowering =====
-
- private lowerMethod(className: String, md: MethodDecl) {
- String fnName = "${className}__${md.name}";
- this.beginFn(fnName, IRType.I64);
- this.resetFnContext();
- this.curClass = className;
-
- if (!md.isStatic) {
- this.addParamToLast("__this", IRType.PTR);
- this.thisReg = "__this";
- }
- Integer pi = 0;
- while (pi < md.params.length()) {
- Param p = md.params.get(pi) as Param;
- this.addParamToLast(p.name, IRType.I64);
- pi = pi + 1;
- }
-
- this.emit(IRInstr.label("entry"));
-
- if (!md.isStatic) {
- this.varDefine("__this", "__this");
- this.varSetClass("__this", className);
- }
- Integer pi2 = 0;
- while (pi2 < md.params.length()) {
- Param p2 = md.params.get(pi2) as Param;
- this.varDefine(p2.name, p2.name);
- String pCls = this.tyToClass(p2.ty);
- if (pCls != "") { this.varSetClass(p2.name, pCls); }
- pi2 = pi2 + 1;
- }
-
- this.lowerBody(md.body);
- IRFunction curFn = this.lastFn();
- Boolean needsRet = true;
- if (curFn.instrs.length() > 0) {
- IRInstr lastInstr = curFn.instrs.get(curFn.instrs.length() - 1) as IRInstr;
- if (lastInstr.op == IROpcode.RET) { needsRet = false; }
- }
- if (needsRet) {
- this.emit(IRInstr.ret(IRValue.ofInt(0, IRType.I64)));
- }
-
- if (md.isStatic && md.name == "main" && this.mainFn == "") {
- this.mainFn = fnName;
- }
- }
-
- private lowerConstructor(className: String, ctor: ConstructorDecl) {
- String fnName = "${className}__init";
- this.beginFn(fnName, IRType.VOID);
- this.resetFnContext();
- this.curClass = className;
-
- this.addParamToLast("__this", IRType.PTR);
- this.thisReg = "__this";
- Integer pi = 0;
- while (pi < ctor.params.length()) {
- Param p = ctor.params.get(pi) as Param;
- this.addParamToLast(p.name, IRType.I64);
- pi = pi + 1;
- }
-
- this.emit(IRInstr.label("entry"));
- this.varDefine("__this", "__this");
- this.varSetClass("__this", className);
- Integer pi2 = 0;
- while (pi2 < ctor.params.length()) {
- Param p2 = ctor.params.get(pi2) as Param;
- this.varDefine(p2.name, p2.name);
- String pCls = this.tyToClass(p2.ty);
- if (pCls != "") { this.varSetClass(p2.name, pCls); }
- pi2 = pi2 + 1;
- }
-
- this.lowerBody(ctor.body);
- this.emit(IRInstr.retVoid());
- }
-
- private lowerClass(cd: ClassDecl) {
- if (cd.ctor != null) {
- ConstructorDecl ctor = cd.ctor as ConstructorDecl;
- if (ctor.body != null) {
- this.lowerConstructor(cd.name, ctor);
- }
- }
- Integer i = 0;
- while (i < cd.methods.length()) {
- MethodDecl md = cd.methods.get(i) as MethodDecl;
- if (md.body != null) {
- this.lowerMethod(cd.name, md);
- }
- i = i + 1;
- }
- }
-
- private lowerModule(m: ArimoModule) {
- Integer i = 0;
- while (i < m.items.length()) {
- Item it = m.items.get(i) as Item;
- if (it.kind == ItemKind.CLASS) {
- this.lowerClass(it.classDecl);
- }
- i = i + 1;
- }
- }
-
- // ===== Print-int helpers (avoid two-call register pressure in caller) =====
-
- private generatePrintlnInt(name: String, addNewline: Boolean) {
- this.beginFn(name, IRType.VOID);
- this.addParamToLast("n", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
-
- IRValue nv = IRValue.reg("n", IRType.I64);
-
- // Convert to string
- String tsReg = this.newReg();
- IRValue tsDst = IRValue.reg(tsReg, IRType.PTR);
- List tsArgs = List();
- tsArgs.append(nv);
- this.emit(IRInstr.call(tsDst, "__arimo_i64_to_str", tsArgs));
-
- // Print
- String target = "__arimo_println";
- if (!addNewline) { target = "__arimo_print"; }
- List callArgs = List();
- callArgs.append(tsDst);
- String dr = this.newReg();
- IRValue dst = IRValue.reg(dr, IRType.VOID);
- this.emit(IRInstr.call(dst, target, callArgs));
- this.emit(IRInstr.retVoid());
- }
-
- // ===== Print helpers =====
-
- private generatePrintHelper(name: String, addNewline: Boolean) {
- this.beginFn(name, IRType.VOID);
- this.addParamToLast("str", IRType.PTR);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
-
- IRValue strSaved = IRValue.reg("str_saved", IRType.PTR);
- this.emit(IRInstr.mov(strSaved, IRValue.reg("str", IRType.PTR)));
-
- String nlName = this.internStr("\n");
- IRValue strPtr = IRValue.reg("str_saved", IRType.PTR);
-
- String lenReg = "plen";
- IRValue lenDst = IRValue.reg(lenReg, IRType.I64);
- List lenArgs = List();
- lenArgs.append(strPtr);
- this.emit(IRInstr.call(lenDst, "__arimo_strlen", lenArgs));
-
- if (this.linux) {
- // write(1, str, len)
- List wrArgs = List();
- wrArgs.append(IRValue.ofInt(1, IRType.I64));
- wrArgs.append(strPtr);
- wrArgs.append(IRValue.reg("plen", IRType.I64));
- IRValue wrDst = IRValue.reg("pwr", IRType.VOID);
- this.emit(IRInstr.syscall(wrDst, 1, wrArgs));
- if (addNewline) {
- List wrArgs2 = List();
- wrArgs2.append(IRValue.ofInt(1, IRType.I64));
- wrArgs2.append(IRValue.global(nlName));
- wrArgs2.append(IRValue.ofInt(1, IRType.I64));
- IRValue wrDst2 = IRValue.reg("pwr2", IRType.VOID);
- this.emit(IRInstr.syscall(wrDst2, 1, wrArgs2));
- }
- } else {
- IRValue hDst = IRValue.reg("ph_h", IRType.I64);
- List hsArgs = List();
- hsArgs.append(IRValue.ofInt(-11, IRType.I64));
- this.emit(IRInstr.call(hDst, "__ext__GetStdHandle", hsArgs));
-
- IRValue bwPtr = IRValue.reg("bwPtr", IRType.PTR);
- this.emit(IRInstr.alloc(bwPtr, 4));
-
- List wfArgs = List();
- wfArgs.append(IRValue.reg("ph_h", IRType.I64));
- wfArgs.append(strPtr);
- wfArgs.append(IRValue.reg("plen", IRType.I64));
- wfArgs.append(IRValue.reg("bwPtr", IRType.PTR));
- wfArgs.append(IRValue.ofInt(0, IRType.I64));
- IRValue wfDst = IRValue.reg("pwf", IRType.VOID);
- this.emit(IRInstr.call(wfDst, "__ext__WriteFile", wfArgs));
-
- if (addNewline) {
- List wfArgs2 = List();
- wfArgs2.append(IRValue.reg("ph_h", IRType.I64));
- wfArgs2.append(IRValue.global(nlName));
- wfArgs2.append(IRValue.ofInt(1, IRType.I64));
- wfArgs2.append(IRValue.reg("bwPtr", IRType.PTR));
- wfArgs2.append(IRValue.ofInt(0, IRType.I64));
- IRValue wfDst2 = IRValue.reg("pwf2", IRType.VOID);
- this.emit(IRInstr.call(wfDst2, "__ext__WriteFile", wfArgs2));
- }
- }
- this.emit(IRInstr.retVoid());
- }
-
- // ===== Strlen helper =====
-
- private generateStrlenHelper() {
- this.beginFn("__arimo_strlen", IRType.I64);
- this.addParamToLast("s", IRType.PTR);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue sBase = IRValue.reg("sBase", IRType.PTR);
- this.emit(IRInstr.mov(sBase, IRValue.reg("s", IRType.PTR)));
- IRValue cnt = IRValue.reg("cnt", IRType.I64);
- this.emit(IRInstr.mov(cnt, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.label("strlen_loop"));
- IRValue idx = IRValue.reg("cidx", IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, idx, IRValue.reg("sBase", IRType.PTR), IRValue.reg("cnt", IRType.I64)));
- IRValue ch = IRValue.reg("ch", IRType.I64);
- this.emit(IRInstr.load(ch, IRValue.reg("cidx", IRType.I64), IRType.I8));
- this.emit(IRInstr.cmp(IRValue.reg("ch", IRType.I64), IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, "strlen_done"));
- IRValue cnt2 = IRValue.reg("cnt", IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, cnt2, IRValue.reg("cnt", IRType.I64), IRValue.ofInt(1, IRType.I64)));
- IRValue sKeep = IRValue.reg("sKeep", IRType.PTR);
- this.emit(IRInstr.mov(sKeep, IRValue.reg("sBase", IRType.PTR)));
- this.emit(IRInstr.jmp("strlen_loop"));
- this.emit(IRInstr.label("strlen_done"));
- this.emit(IRInstr.ret(IRValue.reg("cnt", IRType.I64)));
- }
-
- // ===== List runtime =====
-
- private generateListNew() {
- this.beginFn("__arimo_list_new", IRType.PTR);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue listPtr = this.emitHeapAlloc(IRValue.ofInt(24, IRType.I64));
- IRValue dataPtr = this.emitHeapAlloc(IRValue.ofInt(64, IRType.I64));
- this.emitFieldStore(listPtr, 0, dataPtr);
- this.emitFieldStore(listPtr, 8, IRValue.ofInt(0, IRType.I64));
- this.emitFieldStore(listPtr, 16, IRValue.ofInt(8, IRType.I64));
- this.emit(IRInstr.ret(listPtr));
- }
-
- private generateListLength() {
- this.beginFn("__arimo_list_length", IRType.I64);
- this.addParamToLast("list", IRType.PTR);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue listPar = IRValue.reg("list", IRType.PTR);
- IRValue lenVal = this.emitFieldLoad(listPar, 8);
- this.emit(IRInstr.ret(lenVal));
- }
-
- private generateListGet() {
- this.beginFn("__arimo_list_get", IRType.I64);
- this.addParamToLast("list", IRType.PTR);
- this.addParamToLast("idx", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue listPar = IRValue.reg("list", IRType.PTR);
- IRValue idxPar = IRValue.reg("idx", IRType.I64);
- IRValue data = this.emitFieldLoad(listPar, 0);
- String offReg = this.newReg();
- IRValue off = IRValue.reg(offReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MUL, off, idxPar, IRValue.ofInt(8, IRType.I64)));
- String pReg = this.newReg();
- IRValue slot = IRValue.reg(pReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, slot, data, off));
- String elemReg = this.newReg();
- IRValue elem = IRValue.reg(elemReg, IRType.I64);
- this.emit(IRInstr.load(elem, slot, IRType.I64));
- this.emit(IRInstr.ret(elem));
- }
-
- private generateListSet() {
- this.beginFn("__arimo_list_set", IRType.VOID);
- this.addParamToLast("list", IRType.PTR);
- this.addParamToLast("idx", IRType.I64);
- this.addParamToLast("val", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue listPar = IRValue.reg("list", IRType.PTR);
- IRValue idxPar = IRValue.reg("idx", IRType.I64);
- IRValue valPar = IRValue.reg("val", IRType.I64);
- IRValue data = this.emitFieldLoad(listPar, 0);
- String offReg = this.newReg();
- IRValue off = IRValue.reg(offReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MUL, off, idxPar, IRValue.ofInt(8, IRType.I64)));
- String pReg = this.newReg();
- IRValue slot = IRValue.reg(pReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, slot, data, off));
- this.emit(IRInstr.store(valPar, slot, IRType.I64));
- this.emit(IRInstr.retVoid());
- }
-
- private generateListAppend() {
- this.beginFn("__arimo_list_append", IRType.VOID);
- this.addParamToLast("list", IRType.PTR);
- this.addParamToLast("val", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue listPar = IRValue.reg("list", IRType.PTR);
- IRValue valPar = IRValue.reg("val", IRType.I64);
-
- IRValue lenVal = this.emitFieldLoad(listPar, 8);
- IRValue capVal = this.emitFieldLoad(listPar, 16);
-
- String noResL = this.newLabel("no_resize");
- this.emit(IRInstr.cmp(lenVal, capVal));
- this.emit(IRInstr.branch(IROpcode.JL, noResL));
-
- // resize: new_cap = cap*2, alloc new data, copy, update list
- String ncReg = this.newReg();
- IRValue newCap = IRValue.reg(ncReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, newCap, capVal, capVal));
-
- String nszReg = this.newReg();
- IRValue newSz = IRValue.reg(nszReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MUL, newSz, newCap, IRValue.ofInt(8, IRType.I64)));
-
- IRValue newData = this.emitHeapAlloc(newSz);
-
- IRValue oldData = this.emitFieldLoad(listPar, 0);
-
- String ciReg = this.newReg();
- IRValue copyI = IRValue.reg(ciReg, IRType.I64);
- this.emit(IRInstr.mov(copyI, IRValue.ofInt(0, IRType.I64)));
-
- String cpLoopL = this.newLabel("cp_loop");
- String cpDoneL = this.newLabel("cp_done");
- this.emit(IRInstr.label(cpLoopL));
- this.emit(IRInstr.cmp(copyI, lenVal));
- this.emit(IRInstr.branch(IROpcode.JGE, cpDoneL));
-
- String coReg = this.newReg();
- IRValue copyOff = IRValue.reg(coReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MUL, copyOff, copyI, IRValue.ofInt(8, IRType.I64)));
-
- String spReg = this.newReg();
- IRValue srcPtr = IRValue.reg(spReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, srcPtr, oldData, copyOff));
-
- String elReg = this.newReg();
- IRValue elem = IRValue.reg(elReg, IRType.I64);
- this.emit(IRInstr.load(elem, srcPtr, IRType.I64));
-
- String dpReg = this.newReg();
- IRValue dstPtr = IRValue.reg(dpReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, dstPtr, newData, copyOff));
- this.emit(IRInstr.store(elem, dstPtr, IRType.I64));
-
- this.emit(IRInstr.binop(IROpcode.ADD, copyI, copyI, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp(cpLoopL));
- this.emit(IRInstr.label(cpDoneL));
-
- IRValue ndPtr = this.emitFieldPtr(listPar, 0);
- this.emit(IRInstr.store(newData, ndPtr, IRType.I64));
- IRValue ncPtr = this.emitFieldPtr(listPar, 16);
- this.emit(IRInstr.store(newCap, ncPtr, IRType.I64));
-
- this.emit(IRInstr.label(noResL));
-
- IRValue data2 = this.emitFieldLoad(listPar, 0);
- String loReg = this.newReg();
- IRValue lenOff = IRValue.reg(loReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MUL, lenOff, lenVal, IRValue.ofInt(8, IRType.I64)));
- String slReg = this.newReg();
- IRValue slot = IRValue.reg(slReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, slot, data2, lenOff));
- this.emit(IRInstr.store(valPar, slot, IRType.I64));
-
- String nlReg = this.newReg();
- IRValue newLen = IRValue.reg(nlReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, newLen, lenVal, IRValue.ofInt(1, IRType.I64)));
- IRValue lfPtr = this.emitFieldPtr(listPar, 8);
- this.emit(IRInstr.store(newLen, lfPtr, IRType.I64));
- this.emit(IRInstr.retVoid());
- }
-
- private generateListRemoveAt() {
- this.beginFn("__arimo_list_remove_at", IRType.VOID);
- this.addParamToLast("list", IRType.PTR);
- this.addParamToLast("idx", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue listPar = IRValue.reg("list", IRType.PTR);
- IRValue idxPar = IRValue.reg("idx", IRType.I64);
-
- IRValue lenVal = this.emitFieldLoad(listPar, 8);
- IRValue data = this.emitFieldLoad(listPar, 0);
-
- String iReg = this.newReg();
- IRValue shiftI = IRValue.reg(iReg, IRType.I64);
- this.emit(IRInstr.mov(shiftI, idxPar));
-
- String shLoopL = this.newLabel("sh_loop");
- String shDoneL = this.newLabel("sh_done");
-
- String limReg = this.newReg();
- IRValue lim = IRValue.reg(limReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, lim, lenVal, IRValue.ofInt(1, IRType.I64)));
-
- this.emit(IRInstr.label(shLoopL));
- this.emit(IRInstr.cmp(shiftI, lim));
- this.emit(IRInstr.branch(IROpcode.JGE, shDoneL));
-
- String no1Reg = this.newReg();
- IRValue nextOff = IRValue.reg(no1Reg, IRType.I64);
- String ni1Reg = this.newReg();
- IRValue nextIdx = IRValue.reg(ni1Reg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, nextIdx, shiftI, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.binop(IROpcode.MUL, nextOff, nextIdx, IRValue.ofInt(8, IRType.I64)));
-
- String np1Reg = this.newReg();
- IRValue nextPtr = IRValue.reg(np1Reg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, nextPtr, data, nextOff));
-
- String ev1Reg = this.newReg();
- IRValue elemVal = IRValue.reg(ev1Reg, IRType.I64);
- this.emit(IRInstr.load(elemVal, nextPtr, IRType.I64));
-
- String co1Reg = this.newReg();
- IRValue currOff = IRValue.reg(co1Reg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MUL, currOff, shiftI, IRValue.ofInt(8, IRType.I64)));
- String cp1Reg = this.newReg();
- IRValue currPtr = IRValue.reg(cp1Reg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, currPtr, data, currOff));
- this.emit(IRInstr.store(elemVal, currPtr, IRType.I64));
-
- this.emit(IRInstr.binop(IROpcode.ADD, shiftI, shiftI, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp(shLoopL));
- this.emit(IRInstr.label(shDoneL));
-
- String nl2Reg = this.newReg();
- IRValue newLen = IRValue.reg(nl2Reg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, newLen, lenVal, IRValue.ofInt(1, IRType.I64)));
- IRValue lfPtr = this.emitFieldPtr(listPar, 8);
- this.emit(IRInstr.store(newLen, lfPtr, IRType.I64));
- this.emit(IRInstr.retVoid());
- }
-
- // ===== String builtins =====
-
- private generateStrCmp() {
- this.beginFn("__arimo_strcmp", IRType.I64);
- this.addParamToLast("a", IRType.PTR);
- this.addParamToLast("b", IRType.PTR);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue aP = IRValue.reg("a", IRType.PTR);
- IRValue bP = IRValue.reg("b", IRType.PTR);
- IRValue i = IRValue.reg("sci", IRType.I64);
- this.emit(IRInstr.mov(i, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.label("scmp_loop"));
-
- String pca = this.newReg();
- IRValue pa = IRValue.reg(pca, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, pa, aP, i));
- String cca = this.newReg();
- IRValue ca = IRValue.reg(cca, IRType.I64);
- this.emit(IRInstr.load(ca, pa, IRType.I8));
-
- String pcb = this.newReg();
- IRValue pb = IRValue.reg(pcb, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, pb, bP, i));
- String ccb = this.newReg();
- IRValue cb = IRValue.reg(ccb, IRType.I64);
- this.emit(IRInstr.load(cb, pb, IRType.I8));
-
- this.emit(IRInstr.cmp(ca, cb));
- this.emit(IRInstr.branch(IROpcode.JNE, "scmp_ne"));
-
- this.emit(IRInstr.cmp(ca, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, "scmp_eq"));
-
- this.emit(IRInstr.binop(IROpcode.ADD, i, i, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp("scmp_loop"));
-
- this.emit(IRInstr.label("scmp_ne"));
- String dr1 = this.newReg();
- IRValue diff = IRValue.reg(dr1, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, diff, ca, cb));
- this.emit(IRInstr.ret(diff));
-
- this.emit(IRInstr.label("scmp_eq"));
- this.emit(IRInstr.ret(IRValue.ofInt(0, IRType.I64)));
- }
-
- private generateStrCat() {
- this.beginFn("__arimo_strcat", IRType.PTR);
- this.addParamToLast("a", IRType.PTR);
- this.addParamToLast("b", IRType.PTR);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue aP = IRValue.reg("a", IRType.PTR);
- IRValue bP = IRValue.reg("b", IRType.PTR);
-
- // alen = strlen(a)
- String alenReg = this.newReg();
- IRValue alen = IRValue.reg(alenReg, IRType.I64);
- List sla = List();
- sla.append(aP);
- this.emit(IRInstr.call(alen, "__arimo_strlen", sla));
-
- // blen = strlen(b)
- String blenReg = this.newReg();
- IRValue blen = IRValue.reg(blenReg, IRType.I64);
- List slb = List();
- slb.append(bP);
- this.emit(IRInstr.call(blen, "__arimo_strlen", slb));
-
- // total = alen + blen + 1
- String tlReg = this.newReg();
- IRValue total = IRValue.reg(tlReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, total, alen, blen));
- String tl2Reg = this.newReg();
- IRValue total2 = IRValue.reg(tl2Reg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, total2, total, IRValue.ofInt(1, IRType.I64)));
-
- IRValue buf = this.emitHeapAlloc(total2);
-
- // copy a
- String aiReg = this.newReg();
- IRValue ai = IRValue.reg(aiReg, IRType.I64);
- this.emit(IRInstr.mov(ai, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.label("scat_a"));
- this.emit(IRInstr.cmp(ai, alen));
- this.emit(IRInstr.branch(IROpcode.JGE, "scat_a_done"));
- String apReg = this.newReg();
- IRValue apv = IRValue.reg(apReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, apv, aP, ai));
- String acReg = this.newReg();
- IRValue ac = IRValue.reg(acReg, IRType.I64);
- this.emit(IRInstr.load(ac, apv, IRType.I8));
- String dpReg = this.newReg();
- IRValue dpv = IRValue.reg(dpReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, dpv, buf, ai));
- this.emit(IRInstr.store(ac, dpv, IRType.I8));
- this.emit(IRInstr.binop(IROpcode.ADD, ai, ai, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp("scat_a"));
- this.emit(IRInstr.label("scat_a_done"));
-
- // copy b
- String biReg = this.newReg();
- IRValue bi = IRValue.reg(biReg, IRType.I64);
- this.emit(IRInstr.mov(bi, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.label("scat_b"));
- this.emit(IRInstr.cmp(bi, blen));
- this.emit(IRInstr.branch(IROpcode.JGE, "scat_b_done"));
- String bpReg = this.newReg();
- IRValue bpv = IRValue.reg(bpReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, bpv, bP, bi));
- String bcReg = this.newReg();
- IRValue bc = IRValue.reg(bcReg, IRType.I64);
- this.emit(IRInstr.load(bc, bpv, IRType.I8));
- String dbReg = this.newReg();
- IRValue dbv = IRValue.reg(dbReg, IRType.PTR);
- String dbsReg = this.newReg();
- IRValue dbs = IRValue.reg(dbsReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, dbs, alen, bi));
- this.emit(IRInstr.binop(IROpcode.ADD, dbv, buf, dbs));
- this.emit(IRInstr.store(bc, dbv, IRType.I8));
- this.emit(IRInstr.binop(IROpcode.ADD, bi, bi, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp("scat_b"));
- this.emit(IRInstr.label("scat_b_done"));
-
- // null terminate
- String nullPReg = this.newReg();
- IRValue nullP = IRValue.reg(nullPReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, nullP, buf, total));
- this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), nullP, IRType.I8));
-
- this.emit(IRInstr.ret(buf));
- }
-
- private generateSubstr() {
- this.beginFn("__arimo_substr", IRType.PTR);
- this.addParamToLast("s", IRType.PTR);
- this.addParamToLast("from", IRType.I64);
- this.addParamToLast("to", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue sP = IRValue.reg("s", IRType.PTR);
- IRValue fromP = IRValue.reg("from", IRType.I64);
- IRValue toP = IRValue.reg("to", IRType.I64);
-
- String lenReg = this.newReg();
- IRValue len = IRValue.reg(lenReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, len, toP, fromP));
-
- String aszReg = this.newReg();
- IRValue asz = IRValue.reg(aszReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, asz, len, IRValue.ofInt(1, IRType.I64)));
-
- IRValue buf = this.emitHeapAlloc(asz);
-
- String iReg = this.newReg();
- IRValue si = IRValue.reg(iReg, IRType.I64);
- this.emit(IRInstr.mov(si, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.label("sub_loop"));
- this.emit(IRInstr.cmp(si, len));
- this.emit(IRInstr.branch(IROpcode.JGE, "sub_done"));
-
- String srcIdxReg = this.newReg();
- IRValue srcIdx = IRValue.reg(srcIdxReg, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, srcIdx, fromP, si));
- String spReg = this.newReg();
- IRValue sp = IRValue.reg(spReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, sp, sP, srcIdx));
- String chReg = this.newReg();
- IRValue ch = IRValue.reg(chReg, IRType.I64);
- this.emit(IRInstr.load(ch, sp, IRType.I8));
- String dpReg = this.newReg();
- IRValue dp = IRValue.reg(dpReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, dp, buf, si));
- this.emit(IRInstr.store(ch, dp, IRType.I8));
- this.emit(IRInstr.binop(IROpcode.ADD, si, si, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp("sub_loop"));
- this.emit(IRInstr.label("sub_done"));
-
- String nPReg = this.newReg();
- IRValue nP = IRValue.reg(nPReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, nP, buf, len));
- this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), nP, IRType.I8));
- this.emit(IRInstr.ret(buf));
- }
-
- private generateStartsWith() {
- this.beginFn("__arimo_startswith", IRType.I64);
- this.addParamToLast("s", IRType.PTR);
- this.addParamToLast("prefix", IRType.PTR);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue sP = IRValue.reg("s", IRType.PTR);
- IRValue pfP = IRValue.reg("prefix", IRType.PTR);
- IRValue si = IRValue.reg("swi", IRType.I64);
- this.emit(IRInstr.mov(si, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.label("sw_loop"));
-
- String ppp = this.newReg();
- IRValue pp = IRValue.reg(ppp, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, pp, pfP, si));
- String pcc = this.newReg();
- IRValue pc = IRValue.reg(pcc, IRType.I64);
- this.emit(IRInstr.load(pc, pp, IRType.I8));
- this.emit(IRInstr.cmp(pc, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, "sw_yes"));
-
- String spp = this.newReg();
- IRValue sp = IRValue.reg(spp, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, sp, sP, si));
- String scc = this.newReg();
- IRValue sc = IRValue.reg(scc, IRType.I64);
- this.emit(IRInstr.load(sc, sp, IRType.I8));
- this.emit(IRInstr.cmp(sc, pc));
- this.emit(IRInstr.branch(IROpcode.JNE, "sw_no"));
-
- this.emit(IRInstr.binop(IROpcode.ADD, si, si, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp("sw_loop"));
- this.emit(IRInstr.label("sw_yes"));
- this.emit(IRInstr.ret(IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.label("sw_no"));
- this.emit(IRInstr.ret(IRValue.ofInt(0, IRType.I64)));
- }
-
- private generateEndsWith() {
- this.beginFn("__arimo_endswith", IRType.I64);
- this.addParamToLast("s", IRType.PTR);
- this.addParamToLast("suffix", IRType.PTR);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue sP = IRValue.reg("s", IRType.PTR);
- IRValue sfP = IRValue.reg("suffix", IRType.PTR);
-
- String slenR = this.newReg();
- IRValue slen = IRValue.reg(slenR, IRType.I64);
- List sla = List();
- sla.append(sP);
- this.emit(IRInstr.call(slen, "__arimo_strlen", sla));
-
- String sflenR = this.newReg();
- IRValue sflen = IRValue.reg(sflenR, IRType.I64);
- List slb = List();
- slb.append(sfP);
- this.emit(IRInstr.call(sflen, "__arimo_strlen", slb));
-
- this.emit(IRInstr.cmp(sflen, slen));
- this.emit(IRInstr.branch(IROpcode.JG, "ew_no"));
-
- String offR = this.newReg();
- IRValue off = IRValue.reg(offR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, off, slen, sflen));
-
- IRValue ewi = IRValue.reg("ewi", IRType.I64);
- this.emit(IRInstr.mov(ewi, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.label("ew_loop"));
-
- String sfpp = this.newReg();
- IRValue sfp = IRValue.reg(sfpp, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, sfp, sfP, ewi));
- String sfcc = this.newReg();
- IRValue sfc = IRValue.reg(sfcc, IRType.I64);
- this.emit(IRInstr.load(sfc, sfp, IRType.I8));
- this.emit(IRInstr.cmp(sfc, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, "ew_yes"));
-
- String sidxR = this.newReg();
- IRValue sidx = IRValue.reg(sidxR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, sidx, off, ewi));
- String spp = this.newReg();
- IRValue sp = IRValue.reg(spp, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, sp, sP, sidx));
- String scc = this.newReg();
- IRValue sc = IRValue.reg(scc, IRType.I64);
- this.emit(IRInstr.load(sc, sp, IRType.I8));
- this.emit(IRInstr.cmp(sc, sfc));
- this.emit(IRInstr.branch(IROpcode.JNE, "ew_no"));
-
- this.emit(IRInstr.binop(IROpcode.ADD, ewi, ewi, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp("ew_loop"));
- this.emit(IRInstr.label("ew_yes"));
- this.emit(IRInstr.ret(IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.label("ew_no"));
- this.emit(IRInstr.ret(IRValue.ofInt(0, IRType.I64)));
- }
-
- private generateIndexOf() {
- this.beginFn("__arimo_indexof_from", IRType.I64);
- this.addParamToLast("s", IRType.PTR);
- this.addParamToLast("sub", IRType.PTR);
- this.addParamToLast("from", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue sP = IRValue.reg("s", IRType.PTR);
- IRValue subP = IRValue.reg("sub", IRType.PTR);
- IRValue fromV = IRValue.reg("from", IRType.I64);
-
- String slenR = this.newReg();
- IRValue slen = IRValue.reg(slenR, IRType.I64);
- List sla = List();
- sla.append(sP);
- this.emit(IRInstr.call(slen, "__arimo_strlen", sla));
-
- String sublenR = this.newReg();
- IRValue sublen = IRValue.reg(sublenR, IRType.I64);
- List slb = List();
- slb.append(subP);
- this.emit(IRInstr.call(sublen, "__arimo_strlen", slb));
-
- IRValue oi = IRValue.reg("io_i", IRType.I64);
- this.emit(IRInstr.mov(oi, fromV));
- this.emit(IRInstr.label("io_outer"));
-
- String limR = this.newReg();
- IRValue lim = IRValue.reg(limR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, lim, slen, sublen));
- this.emit(IRInstr.cmp(oi, lim));
- this.emit(IRInstr.branch(IROpcode.JG, "io_notfound"));
-
- IRValue ij = IRValue.reg("io_j", IRType.I64);
- this.emit(IRInstr.mov(ij, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.label("io_inner"));
-
- String spjR = this.newReg();
- IRValue spj = IRValue.reg(spjR, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, spj, subP, ij));
- String scjR = this.newReg();
- IRValue scj = IRValue.reg(scjR, IRType.I64);
- this.emit(IRInstr.load(scj, spj, IRType.I8));
- this.emit(IRInstr.cmp(scj, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, "io_match"));
-
- String soffR = this.newReg();
- IRValue soff = IRValue.reg(soffR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, soff, oi, ij));
- String sppR = this.newReg();
- IRValue spp = IRValue.reg(sppR, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, spp, sP, soff));
- String sccR = this.newReg();
- IRValue scc = IRValue.reg(sccR, IRType.I64);
- this.emit(IRInstr.load(scc, spp, IRType.I8));
- this.emit(IRInstr.cmp(scc, scj));
- this.emit(IRInstr.branch(IROpcode.JNE, "io_nomatch"));
-
- this.emit(IRInstr.binop(IROpcode.ADD, ij, ij, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp("io_inner"));
-
- this.emit(IRInstr.label("io_match"));
- this.emit(IRInstr.ret(oi));
-
- this.emit(IRInstr.label("io_nomatch"));
- this.emit(IRInstr.binop(IROpcode.ADD, oi, oi, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp("io_outer"));
-
- this.emit(IRInstr.label("io_notfound"));
- this.emit(IRInstr.ret(IRValue.ofInt(-1, IRType.I64)));
- }
-
- private generateCharCodeAt() {
- this.beginFn("__arimo_charcodeat", IRType.I64);
- this.addParamToLast("s", IRType.PTR);
- this.addParamToLast("idx", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue sP = IRValue.reg("s", IRType.PTR);
- IRValue idx = IRValue.reg("idx", IRType.I64);
- String pReg = this.newReg();
- IRValue p = IRValue.reg(pReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, p, sP, idx));
- String cReg = this.newReg();
- IRValue c = IRValue.reg(cReg, IRType.I64);
- this.emit(IRInstr.load(c, p, IRType.I8));
- this.emit(IRInstr.ret(c));
- }
-
- private generateCharAt() {
- this.beginFn("__arimo_charat", IRType.PTR);
- this.addParamToLast("s", IRType.PTR);
- this.addParamToLast("idx", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue sP = IRValue.reg("s", IRType.PTR);
- IRValue idx = IRValue.reg("idx", IRType.I64);
- String pReg = this.newReg();
- IRValue p = IRValue.reg(pReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, p, sP, idx));
- String cReg = this.newReg();
- IRValue c = IRValue.reg(cReg, IRType.I64);
- this.emit(IRInstr.load(c, p, IRType.I8));
- IRValue buf = this.emitHeapAlloc(IRValue.ofInt(2, IRType.I64));
- this.emit(IRInstr.store(c, buf, IRType.I8));
- String npReg = this.newReg();
- IRValue np = IRValue.reg(npReg, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, np, buf, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), np, IRType.I8));
- this.emit(IRInstr.ret(buf));
- }
-
- private generateI64ToStr() {
- this.beginFn("__arimo_i64_to_str", IRType.PTR);
- this.addParamToLast("n", IRType.I64);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- IRValue nv = IRValue.reg("n", IRType.I64);
-
- // Special case: n == 0
- this.emit(IRInstr.cmp(nv, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JNE, "its_nonzero"));
- IRValue zeroBuf = this.emitHeapAlloc(IRValue.ofInt(4, IRType.I64));
- String zp0R = this.newReg();
- IRValue zp0 = IRValue.reg(zp0R, IRType.PTR);
- this.emit(IRInstr.mov(zp0, zeroBuf));
- this.emit(IRInstr.store(IRValue.ofInt(48, IRType.I64), zp0, IRType.I8)); // '0'=48
- String zp1R = this.newReg();
- IRValue zp1 = IRValue.reg(zp1R, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, zp1, zeroBuf, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), zp1, IRType.I8));
- this.emit(IRInstr.ret(zeroBuf));
-
- this.emit(IRInstr.label("its_nonzero"));
- // neg = 0; abs_n = n; if n < 0: neg=1, abs_n = 0-n
- IRValue neg = IRValue.reg("its_neg", IRType.I64);
- IRValue abs_n = IRValue.reg("its_absn", IRType.I64);
- this.emit(IRInstr.mov(neg, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.mov(abs_n, nv));
- this.emit(IRInstr.cmp(nv, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JGE, "its_pos"));
- this.emit(IRInstr.mov(neg, IRValue.ofInt(1, IRType.I64)));
- String anR = this.newReg();
- IRValue an = IRValue.reg(anR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, an, IRValue.ofInt(0, IRType.I64), nv));
- this.emit(IRInstr.mov(abs_n, an));
- this.emit(IRInstr.label("its_pos"));
-
- // Count digits
- IRValue cnt = IRValue.reg("its_cnt", IRType.I64);
- IRValue tmp = IRValue.reg("its_tmp", IRType.I64);
- this.emit(IRInstr.mov(cnt, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.mov(tmp, abs_n));
- this.emit(IRInstr.label("its_count"));
- this.emit(IRInstr.cmp(tmp, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JLE, "its_counted"));
- this.emit(IRInstr.binop(IROpcode.ADD, cnt, cnt, IRValue.ofInt(1, IRType.I64)));
- String dvrR = this.newReg();
- IRValue dvr = IRValue.reg(dvrR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.DIV, dvr, tmp, IRValue.ofInt(10, IRType.I64)));
- this.emit(IRInstr.mov(tmp, dvr));
- this.emit(IRInstr.jmp("its_count"));
- this.emit(IRInstr.label("its_counted"));
-
- // total = cnt + neg
- String totR = this.newReg();
- IRValue tot = IRValue.reg(totR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, tot, cnt, neg));
- String aszR = this.newReg();
- IRValue asz = IRValue.reg(aszR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, asz, tot, IRValue.ofInt(1, IRType.I64)));
-
- IRValue buf = this.emitHeapAlloc(asz);
-
- // null terminate
- String nlPR = this.newReg();
- IRValue nlP = IRValue.reg(nlPR, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, nlP, buf, tot));
- this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), nlP, IRType.I8));
-
- // write '-' if neg
- this.emit(IRInstr.cmp(neg, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JE, "its_no_neg"));
- String np0R = this.newReg();
- IRValue np0 = IRValue.reg(np0R, IRType.PTR);
- this.emit(IRInstr.mov(np0, buf));
- this.emit(IRInstr.store(IRValue.ofInt(45, IRType.I64), np0, IRType.I8)); // '-'=45
- this.emit(IRInstr.label("its_no_neg"));
-
- // write digits right to left: pos = tot-1
- IRValue pos = IRValue.reg("its_pos", IRType.I64);
- IRValue abs2 = IRValue.reg("its_ab2", IRType.I64);
- this.emit(IRInstr.binop(IROpcode.SUB, pos, tot, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.mov(abs2, abs_n));
- this.emit(IRInstr.label("its_write"));
- this.emit(IRInstr.cmp(abs2, IRValue.ofInt(0, IRType.I64)));
- this.emit(IRInstr.branch(IROpcode.JLE, "its_written"));
- String modR = this.newReg();
- IRValue md = IRValue.reg(modR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.MOD, md, abs2, IRValue.ofInt(10, IRType.I64)));
- String digR = this.newReg();
- IRValue dig = IRValue.reg(digR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.ADD, dig, md, IRValue.ofInt(48, IRType.I64))); // '0'=48
- String dPR = this.newReg();
- IRValue dP = IRValue.reg(dPR, IRType.PTR);
- this.emit(IRInstr.binop(IROpcode.ADD, dP, buf, pos));
- this.emit(IRInstr.store(dig, dP, IRType.I8));
- String divR = this.newReg();
- IRValue dv = IRValue.reg(divR, IRType.I64);
- this.emit(IRInstr.binop(IROpcode.DIV, dv, abs2, IRValue.ofInt(10, IRType.I64)));
- this.emit(IRInstr.mov(abs2, dv));
- this.emit(IRInstr.binop(IROpcode.SUB, pos, pos, IRValue.ofInt(1, IRType.I64)));
- this.emit(IRInstr.jmp("its_write"));
- this.emit(IRInstr.label("its_written"));
- this.emit(IRInstr.ret(buf));
- }
-
- // ===== Entry point =====
-
- private generateStartFn() {
- this.beginFn("_start", IRType.VOID);
- this.resetFnContext();
- this.emit(IRInstr.label("entry"));
- List mainArgs = List();
- IRValue mainResult = IRValue.reg("main_result", IRType.I64);
- if (this.mainFn != "") {
- this.emit(IRInstr.call(mainResult, this.mainFn, mainArgs));
- } else {
- this.emit(IRInstr.mov(mainResult, IRValue.ofInt(0, IRType.I64)));
- }
- List exitArgs = List();
- exitArgs.append(mainResult);
- IRValue exitDst = IRValue.reg("ex", IRType.VOID);
- if (this.linux) {
- this.emit(IRInstr.syscall(exitDst, 60, exitArgs));
- } else {
- this.emit(IRInstr.call(exitDst, "__ext__ExitProcess", exitArgs));
- }
- this.emit(IRInstr.retVoid());
- }
-
- public lower(modules: List) {
- this.registerClasses(modules);
- this.generateTeardownRoutines();
- this.internStr("\n");
- Integer i = 0;
- while (i < modules.length()) {
- ArimoModule m = modules.get(i) as ArimoModule;
- this.lowerModule(m);
- i = i + 1;
- }
- this.generateStrlenHelper();
- this.generateListNew();
- this.generateListLength();
- this.generateListGet();
- this.generateListSet();
- this.generateListAppend();
- this.generateListRemoveAt();
- this.generateStrCmp();
- this.generateStrCat();
- this.generateSubstr();
- this.generateStartsWith();
- this.generateEndsWith();
- this.generateIndexOf();
- this.generateCharCodeAt();
- this.generateCharAt();
- this.generateI64ToStr();
- this.generatePrintlnInt("__arimo_println_int", true);
- this.generatePrintlnInt("__arimo_print_int", false);
- this.generatePrintHelper("__arimo_println", true);
- this.generatePrintHelper("__arimo_print", false);
- this.generateStartFn();
- }
-}
+/*
+Arimo Lang Compiler - A modern programming language and compiler
+Copyright (C) 2026 Egecan Akıncıoğlu
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published
+by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+*/
+
+package arimo.compiler.backend;
+
+import arimo.compiler.ast.ArimoModule;
+import arimo.compiler.ast.item.Item;
+import arimo.compiler.ast.item.ItemKind;
+import arimo.compiler.ast.item.ClassDecl;
+import arimo.compiler.ast.decl.MethodDecl;
+import arimo.compiler.ast.decl.FieldDecl;
+import arimo.compiler.ast.decl.ConstructorDecl;
+import arimo.compiler.ast.decl.Param;
+import arimo.compiler.ast.stmt.Stmt;
+import arimo.compiler.ast.stmt.StmtKind;
+import arimo.compiler.ast.stmt.ElseIfBranch;
+import arimo.compiler.ast.expr.Expr;
+import arimo.compiler.ast.expr.ExprKind;
+import arimo.compiler.ast.expr.BinaryOp;
+import arimo.compiler.ast.expr.StrPart;
+import arimo.compiler.ast.types.AstType;
+import arimo.compiler.ast.types.TypeKind;
+import arimo.compiler.backend.IRFunction;
+import arimo.compiler.backend.IRInstr;
+import arimo.compiler.backend.IRValue;
+import arimo.compiler.backend.IRValueKind;
+import arimo.compiler.backend.IROpcode;
+import arimo.compiler.backend.IRType;
+
+public class IRLower {
+ public irFns : List;
+ public strNames : List;
+ public strConts : List;
+ public bssNames : List;
+ public bssSizes : List;
+ private strCnt : Integer;
+ private labelCnt : Integer;
+ private regCnt : Integer;
+ private varNames : List;
+ private varRegs : List;
+ private varClassNames : List;
+ private breakLbl : String;
+ private contLbl : String;
+ private breakDepth : Integer;
+ private contDepth : Integer;
+ private scopeStack : List>;
+ private mainFn : String;
+ private classNames : List;
+ private classParents : List;
+ private allFieldNames : List;
+ private allFieldClasses: List;
+ private classFldStarts : List;
+ private classFldCounts : List;
+ private methodRetNames : List;
+ private methodRetClasses: List;
+ private teardownLabels : List;
+ private curClass : String;
+ private thisReg : String;
+ private linux : Boolean;
+
+ public constructor(linux: Boolean) {
+ this.linux = linux;
+ this.irFns = List();
+ this.strNames = List();
+ this.strConts = List();
+ this.bssNames = List();
+ this.bssSizes = List();
+ this.strCnt = 0;
+ this.labelCnt = 0;
+ this.regCnt = 0;
+ this.varNames = List();
+ this.varRegs = List();
+ this.varClassNames = List();
+ this.breakLbl = "";
+ this.contLbl = "";
+ this.breakDepth = 0;
+ this.contDepth = 0;
+ this.scopeStack = List();
+ this.mainFn = "";
+ this.classNames = List();
+ this.classParents = List();
+ this.allFieldNames = List();
+ this.allFieldClasses = List();
+ this.classFldStarts = List();
+ this.classFldCounts = List();
+ this.methodRetNames = List();
+ this.methodRetClasses = List();
+ this.teardownLabels = List();
+ this.curClass = "";
+ this.thisReg = "";
+ this.irFns.append(IRFunction("__placeholder", IRType.VOID));
+ }
+
+ // ===== Class layout registration =====
+
+ private registerClasses(modules: List) {
+ Integer mi = 0;
+ while (mi < modules.length()) {
+ ArimoModule m = modules.get(mi) as ArimoModule;
+ Integer ii = 0;
+ while (ii < m.items.length()) {
+ Item it = m.items.get(ii) as Item;
+ if (it.kind == ItemKind.CLASS) {
+ this.registerClass(it.classDecl);
+ }
+ ii = ii + 1;
+ }
+ mi = mi + 1;
+ }
+ }
+
+ private registerClass(cd: ClassDecl) {
+ if (this.classIdxOf(cd.name) >= 0) { return; }
+ Integer start = this.allFieldNames.length();
+ Integer count = 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;
+ }
+ fi = fi + 1;
+ }
+ this.classNames.append(cd.name);
+ this.classFldStarts.append(start);
+ this.classFldCounts.append(count);
+ this.teardownLabels.append("");
+ // Track parent class for recursive field release (inheritance chain)
+ if (cd.extends_ != "" && cd.extends_.length() > 0) {
+ this.classParents.append(this.classIdxOf(cd.extends_));
+ } else {
+ this.classParents.append(-1);
+ }
+
+ // Track method return types for inferClass(METHOD)
+ Integer mi = 0;
+ while (mi < cd.methods.length()) {
+ MethodDecl md = cd.methods.get(mi) as MethodDecl;
+ String retCls = this.tyToClass(md.returnTy);
+ this.methodRetNames.append("${cd.name}__${md.name}");
+ this.methodRetClasses.append(retCls);
+ mi = mi + 1;
+ }
+ }
+
+ private classIdxOf(name: String) : Integer {
+ Integer i = 0;
+ while (i < this.classNames.length()) {
+ String n = this.classNames.get(i) as String;
+ if (n == name) { return i; }
+ i = i + 1;
+ }
+ return -1;
+ }
+
+ private fieldIdxOf(clsIdx: Integer, fieldName: String) : Integer {
+ Integer start = this.classFldStarts.get(clsIdx) as Integer;
+ Integer count = this.classFldCounts.get(clsIdx) as Integer;
+ Integer i = 0;
+ while (i < count) {
+ String fn = this.allFieldNames.get(start + i) as String;
+ if (fn == fieldName) { return i + 1; }
+ i = i + 1;
+ }
+ return -1;
+ }
+
+ private classSizeBytes(clsIdx: Integer) : Integer {
+ Integer count = this.classFldCounts.get(clsIdx) as Integer;
+ return (1 + count) * 8 + 8;
+ }
+
+ private refCountOff(clsIdx: Integer) : Integer {
+ return this.classSizeBytes(clsIdx) - 8;
+ }
+
+ // ===== Variable class tracking =====
+
+ private varClassOf(name: String) : String {
+ Integer i = 0;
+ while (i < this.varNames.length()) {
+ String n = this.varNames.get(i) as String;
+ if (n == name) { return this.varClassNames.get(i) as String; }
+ i = i + 1;
+ }
+ return "";
+ }
+
+ private varSetClass(name: String, cls: String) {
+ Integer i = 0;
+ while (i < this.varNames.length()) {
+ String n = this.varNames.get(i) as String;
+ if (n == name) { this.varClassNames.set(i, cls); return; }
+ i = i + 1;
+ }
+ }
+
+ // ===== Type inference =====
+
+ private tyToClass(ty: AstType) : String {
+ if (ty == null) { return ""; }
+ if (ty.kind == TypeKind.NAMED) { return ty.name; }
+ if (ty.kind == TypeKind.STR) { return "String"; }
+ if (ty.kind == TypeKind.LIST) { return "List"; }
+ if (ty.kind == TypeKind.NULLABLE) { return this.tyToClass(ty.inner); }
+ if (ty.kind == TypeKind.INTEGER) { return "Integer"; }
+ if (ty.kind == TypeKind.BOOLEAN) { return "Boolean"; }
+ if (ty.kind == TypeKind.FLOAT) { return "Float"; }
+ if (ty.kind == TypeKind.CHAR) { return "Char"; }
+ if (ty.kind == TypeKind.I64) { return "I64"; }
+ if (ty.kind == TypeKind.I32) { return "I32"; }
+ if (ty.kind == TypeKind.I16) { return "I16"; }
+ if (ty.kind == TypeKind.I8) { return "I8"; }
+ if (ty.kind == TypeKind.U64) { return "U64"; }
+ if (ty.kind == TypeKind.U32) { return "U32"; }
+ if (ty.kind == TypeKind.U16) { return "U16"; }
+ if (ty.kind == TypeKind.U8) { return "U8"; }
+ return "";
+ }
+
+ private inferClass(expr: Expr) : String {
+ if (expr.kind == ExprKind.THIS) { return this.curClass; }
+ if (expr.kind == ExprKind.IDENT) { return this.varClassOf(expr.strVal); }
+ if (expr.kind == ExprKind.CTOR) { return expr.class_; }
+ if (expr.kind == ExprKind.CAST) {
+ if (expr.castTy.kind == TypeKind.NAMED) { return expr.castTy.name; }
+ if (expr.castTy.kind == TypeKind.STR) { return "String"; }
+ if (expr.castTy.kind == TypeKind.LIST) { return "List"; }
+ if (expr.castTy.kind == TypeKind.NULLABLE) { return this.tyToClass(expr.castTy.inner); }
+ }
+ if (expr.kind == ExprKind.FIELD) {
+ String objCls = this.inferClass(expr.object);
+ Integer clsIdx = this.classIdxOf(objCls);
+ if (clsIdx >= 0) {
+ Integer fldIdx = this.fieldIdxOf(clsIdx, expr.field);
+ if (fldIdx >= 0) {
+ Integer start = this.classFldStarts.get(clsIdx) as Integer;
+ return this.allFieldClasses.get(start + fldIdx - 1) as String;
+ }
+ }
+ return "";
+ }
+ if (expr.kind == ExprKind.METHOD) {
+ String objCls = this.inferClass(expr.object);
+ if (objCls != "") {
+ String key = "${objCls}__${expr.method}";
+ Integer i = 0;
+ while (i < this.methodRetNames.length()) {
+ if (this.methodRetNames.get(i) as String == key) {
+ return this.methodRetClasses.get(i) as String;
+ }
+ i = i + 1;
+ }
+ // Built-in method (String/List): assume method returns receiver type (common for chaining)
+ return objCls;
+ }
+ return "";
+ }
+ return "";
+ }
+
+ private inferStaticCallReturn(className: String, methodName: String) : String {
+ String key = "${className}__${methodName}";
+ Integer i = 0;
+ while (i < this.methodRetNames.length()) {
+ if (this.methodRetNames.get(i) as String == key) {
+ return this.methodRetClasses.get(i) as String;
+ }
+ i = i + 1;
+ }
+ // Known built-in returning String
+ if (className == "Env" && methodName == "platform") { return "String"; }
+ return "";
+ }
+
+ private isIntegerExpr(expr: Expr) : Boolean {
+ if (expr.kind == ExprKind.INT_LIT) { return true; }
+ if (expr.kind == ExprKind.BOOL_LIT) { return true; }
+ if (expr.kind == ExprKind.BINOP) {
+ Integer bop = expr.op;
+ if (bop == BinaryOp.ADD || bop == BinaryOp.SUB ||
+ bop == BinaryOp.MUL || bop == BinaryOp.DIV ||
+ bop == BinaryOp.MOD || bop == BinaryOp.EQ ||
+ bop == BinaryOp.NE || bop == BinaryOp.LT ||
+ bop == BinaryOp.LE || bop == BinaryOp.GT ||
+ bop == BinaryOp.GE) { return true; }
+ }
+ if (expr.kind == ExprKind.METHOD) {
+ String m = expr.method;
+ if (m == "length" || m == "size" || m == "indexOf" ||
+ m == "charCodeAt" || m == "compareTo") { return true; }
+ }
+ return false;
+ }
+
+ private isStringExpr(expr: Expr) : Boolean {
+ if (expr.kind == ExprKind.STR_LIT) { return true; }
+ if (expr.kind == ExprKind.STR_INTERP) { return true; }
+ if (expr.kind == ExprKind.IDENT) {
+ String cls = this.varClassOf(expr.strVal);
+ return cls == "String";
+ }
+ if (expr.kind == ExprKind.FIELD) {
+ String objCls = this.inferClass(expr.object);
+ Integer clsIdx = this.classIdxOf(objCls);
+ if (clsIdx >= 0) {
+ Integer fldIdx = this.fieldIdxOf(clsIdx, expr.field);
+ if (fldIdx >= 0) {
+ Integer start = this.classFldStarts.get(clsIdx) as Integer;
+ String fldCls = this.allFieldClasses.get(start + fldIdx - 1) as String;
+ return fldCls == "String";
+ }
+ }
+ return false;
+ }
+ if (expr.kind == ExprKind.METHOD) {
+ // Check method return type via class metadata first
+ if (this.inferClass(expr) == "String") { return true; }
+ // Check static method call on known class (e.g., Functions.greet())
+ if (expr.object.kind == ExprKind.IDENT) {
+ String key = "${expr.object.strVal}__${expr.method}";
+ Integer ki = 0;
+ while (ki < this.methodRetNames.length()) {
+ if (this.methodRetNames.get(ki) as String == key) {
+ if (this.methodRetClasses.get(ki) as String == "String") { return true; }
+ }
+ ki = ki + 1;
+ }
+ }
+ // Fallback: hardcoded built-in string-returning methods
+ String m = expr.method;
+ if (m == "concat" || m == "substring" || m == "toString" ||
+ m == "toUpperCase" || m == "toLowerCase" || m == "trim" ||
+ m == "charAt") { return true; }
+ return false;
+ }
+ if (expr.kind == ExprKind.STATIC_CALL) {
+ return this.inferStaticCallReturn(expr.class_, expr.method) == "String";
+ }
+ if (expr.kind == ExprKind.CTOR) { return expr.class_ == "String"; }
+ if (expr.kind == ExprKind.CAST) {
+ if (expr.castTy.kind == TypeKind.STR) { return true; }
+ if (expr.castTy.kind == TypeKind.NAMED) { return expr.castTy.name == "String"; }
+ return false;
+ }
+ return false;
+ }
+
+ // ===== Core IR helpers =====
+
+ private beginFn(name: String, retTy: Integer) {
+ this.irFns.append(IRFunction(name, retTy));
+ }
+
+ private lastFn() : IRFunction {
+ Integer last = this.irFns.length() - 1;
+ return this.irFns.get(last) as IRFunction;
+ }
+
+ private addParamToLast(name: String, ty: Integer) {
+ IRFunction f = this.lastFn();
+ f.addParam(name, ty);
+ }
+
+ private newReg() : String {
+ Integer n = this.regCnt;
+ this.regCnt = this.regCnt + 1;
+ return "t${n}";
+ }
+
+ private newLabel(prefix: String) : String {
+ Integer n = this.labelCnt;
+ this.labelCnt = this.labelCnt + 1;
+ return "${prefix}${n}";
+ }
+
+ private internStr(content: String) : String {
+ Integer i = 0;
+ while (i < this.strConts.length()) {
+ String c = this.strConts.get(i) as String;
+ if (c == content) { return this.strNames.get(i) as String; }
+ i = i + 1;
+ }
+ String name = "__str${this.strCnt}";
+ this.strCnt = this.strCnt + 1;
+ this.strNames.append(name);
+ this.strConts.append(content);
+ return name;
+ }
+
+ private emit(instr: IRInstr) {
+ IRFunction f = this.lastFn();
+ f.instrs.append(instr);
+ }
+
+ private varLookup(name: String) : String {
+ Integer i = 0;
+ while (i < this.varNames.length()) {
+ String n = this.varNames.get(i) as String;
+ if (n == name) { return this.varRegs.get(i) as String; }
+ i = i + 1;
+ }
+ return "";
+ }
+
+ private varDefine(name: String, reg: String) {
+ Integer i = 0;
+ while (i < this.varNames.length()) {
+ String n = this.varNames.get(i) as String;
+ if (n == name) { this.varRegs.set(i, reg); return; }
+ i = i + 1;
+ }
+ this.varNames.append(name);
+ this.varRegs.append(reg);
+ this.varClassNames.append("");
+ this.trackScopedVar(name);
+ }
+
+ private resetFnContext() {
+ this.varNames = List();
+ this.varRegs = List();
+ this.varClassNames = List();
+ this.scopeStack = List();
+ this.regCnt = 0;
+ this.curClass = "";
+ this.thisReg = "";
+ }
+
+ // ===== Scope management =====
+
+ private pushScope() {
+ this.scopeStack.append(List());
+ }
+
+ private trackScopedVar(name: String) {
+ Integer n = this.scopeStack.length();
+ if (n > 0) {
+ List top = this.scopeStack.get(n - 1) as List;
+ top.append(name);
+ }
+ }
+
+ private popScope() {
+ Integer n = this.scopeStack.length();
+ if (n == 0) { return; }
+ List vars = this.scopeStack.get(n - 1) as List;
+ Integer i = vars.length();
+ while (i > 0) {
+ i = i - 1;
+ String vn = vars.get(i) as String;
+ // TODO: replace magic-string "__this" with ownership metadata model
+ if (vn == "__this") { continue; }
+ String vc = this.varClassOf(vn);
+ Integer vci = this.classIdxOf(vc);
+ if (vci >= 0) {
+ String vr = this.varLookup(vn);
+ if (vr != "") {
+ this.emitRelease(IRValue.reg(vr, IRType.I64), vci);
+ }
+ }
+ }
+ this.scopeStack.removeAt(n - 1);
+ }
+
+ private emitUnwindToDepth(targetDepth: Integer) {
+ Integer n = this.scopeStack.length();
+ while (n > targetDepth) {
+ this.popScope();
+ n = this.scopeStack.length();
+ }
+ }
+
+ // ===== Heap + field helpers =====
+
+ private emitHeapAlloc(sizeVal: IRValue) : IRValue {
+ if (this.linux) {
+ // mmap(NULL, size, PROT_READ|PROT_WRITE=3, MAP_PRIVATE|MAP_ANONYMOUS=34, -1, 0)
+ String allocReg = this.newReg();
+ IRValue allocDst = IRValue.reg(allocReg, IRType.PTR);
+ List mmapArgs = List();
+ mmapArgs.append(IRValue.ofInt(0, IRType.I64));
+ mmapArgs.append(sizeVal);
+ mmapArgs.append(IRValue.ofInt(3, IRType.I64));
+ mmapArgs.append(IRValue.ofInt(34, IRType.I64));
+ mmapArgs.append(IRValue.ofInt(-1, IRType.I64));
+ mmapArgs.append(IRValue.ofInt(0, IRType.I64));
+ this.emit(IRInstr.syscall(allocDst, 9, mmapArgs));
+ return allocDst;
+ }
+ String phReg = this.newReg();
+ IRValue phDst = IRValue.reg(phReg, IRType.I64);
+ List phArgs = List();
+ this.emit(IRInstr.call(phDst, "__ext__GetProcessHeap", phArgs));
+ String allocReg = this.newReg();
+ IRValue allocDst = IRValue.reg(allocReg, IRType.PTR);
+ List haArgs = List();
+ haArgs.append(phDst);
+ haArgs.append(IRValue.ofInt(0, IRType.I64));
+ haArgs.append(sizeVal);
+ this.emit(IRInstr.call(allocDst, "__ext__HeapAlloc", haArgs));
+ return allocDst;
+ }
+
+ private emitHeapFree(objPtr: IRValue, clsIdx: Integer) {
+ Integer sz = this.classSizeBytes(clsIdx);
+ if (this.linux) {
+ List munmapArgs = List();
+ munmapArgs.append(objPtr);
+ munmapArgs.append(IRValue.ofInt(sz, IRType.I64));
+ String freeReg = this.newReg();
+ IRValue freeDst = IRValue.reg(freeReg, IRType.VOID);
+ this.emit(IRInstr.syscall(freeDst, 11, munmapArgs));
+ } else {
+ String phReg = this.newReg();
+ IRValue phDst = IRValue.reg(phReg, IRType.I64);
+ List phArgs = List();
+ this.emit(IRInstr.call(phDst, "__ext__GetProcessHeap", phArgs));
+ List hfArgs = List();
+ hfArgs.append(phDst);
+ hfArgs.append(IRValue.ofInt(0, IRType.I64));
+ hfArgs.append(objPtr);
+ String freeReg = this.newReg();
+ IRValue freeDst = IRValue.reg(freeReg, IRType.VOID);
+ this.emit(IRInstr.call(freeDst, "__ext__HeapFree", hfArgs));
+ }
+ }
+
+ private emitFieldPtr(objVal: IRValue, offset: Integer) : IRValue {
+ String pReg = this.newReg();
+ IRValue pDst = IRValue.reg(pReg, IRType.PTR);
+ // Compute field pointer via MOV + ADD
+ this.emit(IRInstr.mov(pDst, objVal));
+ this.emit(IRInstr.binop(IROpcode.ADD, pDst, pDst, IRValue.ofInt(offset, IRType.I64)));
+ // Extend objVal live range past pDst def to prevent register alias.
+ // The allocator must not assign pDst the same physical register as live objVal.
+ String guardR = this.newReg();
+ this.emit(IRInstr.binop(IROpcode.ADD, IRValue.reg(guardR, IRType.I64), objVal, IRValue.ofInt(0, IRType.I64)));
+ return pDst;
+ }
+
+ private emitFieldLoad(objVal: IRValue, offset: Integer) : IRValue {
+ IRValue ptr = this.emitFieldPtr(objVal, offset);
+ String vReg = this.newReg();
+ IRValue vDst = IRValue.reg(vReg, IRType.I64);
+ this.emit(IRInstr.load(vDst, ptr, IRType.I64));
+ return vDst;
+ }
+
+ // Safe variants: load/store from pre-computed field pointer (no emitFieldPtr call)
+ private emitFieldLoadS(ptr: IRValue) : IRValue {
+ String vReg = this.newReg();
+ IRValue vDst = IRValue.reg(vReg, IRType.I64);
+ this.emit(IRInstr.load(vDst, ptr, IRType.I64));
+ return vDst;
+ }
+
+ private emitFieldStore(objVal: IRValue, offset: Integer, val: IRValue) {
+ IRValue ptr = this.emitFieldPtr(objVal, offset);
+ this.emit(IRInstr.store(val, ptr, IRType.I64));
+ }
+
+ private emitFieldStoreS(ptr: IRValue, val: IRValue) {
+ this.emit(IRInstr.store(val, ptr, IRType.I64));
+ }
+
+ // ===== ARC retain helper =====
+
+ private emitRetain(objVal: IRValue, clsIdx: Integer) {
+ // Null guard: retain(null) is no-op (prevents SIGSEGV when field param is null)
+ String nullSkipLbl = this.newLabel("ret_null_skip");
+ this.emit(IRInstr.cmp(objVal, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, nullSkipLbl));
+
+ // Inline retain: inc [obj + refCountOff]
+ // Compute refCount field pointer ONCE to avoid emitFieldPtr double-call reg clobber
+ IRValue rcPtr = this.emitFieldPtr(objVal, this.refCountOff(clsIdx));
+ String curR = this.newReg();
+ IRValue curRef = IRValue.reg(curR, IRType.I64);
+ this.emit(IRInstr.load(curRef, rcPtr, IRType.I64));
+ String nr = this.newReg();
+ IRValue newRef = IRValue.reg(nr, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, newRef, curRef, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.store(newRef, rcPtr, IRType.I64));
+
+ this.emit(IRInstr.label(nullSkipLbl));
+ }
+
+ // ===== Cached teardown routine generation =====
+
+ private needsTeardown(clsIdx: Integer) : Boolean {
+ Integer currIdx = clsIdx;
+ while (currIdx >= 0) {
+ Integer start = this.classFldStarts.get(currIdx) as Integer;
+ Integer count = this.classFldCounts.get(currIdx) as Integer;
+ Integer fi = 0;
+ while (fi < count) {
+ String fldCls = this.allFieldClasses.get(start + fi) as String;
+ if (this.classIdxOf(fldCls) >= 0) { return true; }
+ fi = fi + 1;
+ }
+ currIdx = this.classParents.get(currIdx) as Integer;
+ }
+ return false;
+ }
+
+ private emitTeardownBody(clsIdx: Integer) {
+ // obj parameter = first param (RDI on Linux, RCX on Windows)
+ IRValue objParam = IRValue.reg("obj", IRType.PTR);
+
+ Integer currIdx = clsIdx;
+ while (currIdx >= 0) {
+ Integer start = this.classFldStarts.get(currIdx) as Integer;
+ Integer count = this.classFldCounts.get(currIdx) as Integer;
+ Integer fi = count;
+ while (fi > 0) {
+ fi = fi - 1;
+ String fldCls = this.allFieldClasses.get(start + fi) as String;
+ Integer fldClsIdx = this.classIdxOf(fldCls);
+ if (fldClsIdx >= 0) {
+ // Load field value
+ IRValue fldVal = this.emitFieldLoad(objParam, (fi + 1) * 8);
+
+ // Null guard for field
+ String fldSkipLbl = this.newLabel("td_fld_skip");
+ this.emit(IRInstr.cmp(fldVal, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, fldSkipLbl));
+
+ // Decrement field refcount (compute ptr once to avoid reg clobber)
+ IRValue fldRCPtr = this.emitFieldPtr(fldVal, this.refCountOff(fldClsIdx));
+ IRValue fldRC = this.emitFieldLoadS(fldRCPtr);
+ String nr = this.newReg();
+ IRValue newRC = IRValue.reg(nr, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, newRC, fldRC, IRValue.ofInt(1, IRType.I64)));
+ this.emitFieldStoreS(fldRCPtr, newRC);
+
+ // Conditional free: only when field refcount reaches 0
+ this.emit(IRInstr.cmp(newRC, IRValue.ofInt(0, IRType.I64)));
+ String freeSkipLbl = this.newLabel("td_free_skip");
+ this.emit(IRInstr.branch(IROpcode.JNE, freeSkipLbl));
+
+ // CALL child teardown (may be same class → recursive, safe via rc guard)
+ String childTd = this.teardownLabels.get(fldClsIdx) as String;
+ if (childTd != "") {
+ List args = List();
+ args.append(fldVal);
+ String dr = this.newReg();
+ this.emit(IRInstr.call(IRValue.reg(dr, IRType.I64), childTd, args));
+ }
+
+ // Heap free child
+ this.emitHeapFree(fldVal, fldClsIdx);
+ this.emit(IRInstr.label(freeSkipLbl));
+ this.emit(IRInstr.label(fldSkipLbl));
+ }
+ }
+ currIdx = this.classParents.get(currIdx) as Integer;
+ }
+
+ this.emit(IRInstr.retVoid());
+ }
+
+ private generateTeardownRoutines() {
+ Integer i = 0;
+ while (i < this.classNames.length()) {
+ if (this.needsTeardown(i)) {
+ String label = "__arimo_td_${i}";
+ this.teardownLabels.set(i, label);
+ this.beginFn(label, IRType.VOID);
+ this.addParamToLast("obj", IRType.PTR);
+ this.emitTeardownBody(i);
+ }
+ i = i + 1;
+ }
+ }
+
+ // ===== ARC release helper =====
+
+ private emitRelease(objVal: IRValue, clsIdx: Integer) {
+ // Null guard: release(null) is no-op (prevents SIGSEGV on x=null → scope exit)
+ String nullSkipLbl = this.newLabel("rel_null_skip");
+ this.emit(IRInstr.cmp(objVal, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, nullSkipLbl));
+
+ // Inline release: dec [obj + refCountOff]
+ // Compute refCount field pointer ONCE to avoid emitFieldPtr double-call reg clobber
+ IRValue rcPtr2 = this.emitFieldPtr(objVal, this.refCountOff(clsIdx));
+ String curR2 = this.newReg();
+ IRValue curRef2 = IRValue.reg(curR2, IRType.I64);
+ this.emit(IRInstr.load(curRef2, rcPtr2, IRType.I64));
+ String nr = this.newReg();
+ IRValue newRef = IRValue.reg(nr, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, newRef, curRef2, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.store(newRef, rcPtr2, IRType.I64));
+
+ // Conditional free: only when refcount reaches 0
+ this.emit(IRInstr.cmp(newRef, IRValue.ofInt(0, IRType.I64)));
+ String freeSkipLbl = this.newLabel("rel_free_skip");
+ this.emit(IRInstr.branch(IROpcode.JNE, freeSkipLbl));
+
+ // Cached teardown routine: field teardown generated once per class,
+ // called via runtime CALL (replaces exponential inline expansion).
+ String tdLabel = this.teardownLabels.get(clsIdx) as String;
+ if (tdLabel != "") {
+ List tdArgs = List();
+ tdArgs.append(objVal);
+ String tdReg = this.newReg();
+ this.emit(IRInstr.call(IRValue.reg(tdReg, IRType.I64), tdLabel, tdArgs));
+ }
+
+ this.emitHeapFree(objVal, clsIdx);
+ this.emit(IRInstr.label(freeSkipLbl));
+
+ this.emit(IRInstr.label(nullSkipLbl));
+ }
+
+ // ===== Condition lowering =====
+
+ private lowerCond(expr: Expr) : Integer {
+ if (expr.kind == ExprKind.BINOP) {
+ Integer bop = expr.op;
+ if (bop == BinaryOp.EQ || bop == BinaryOp.NE ||
+ bop == BinaryOp.LT || bop == BinaryOp.LE ||
+ bop == BinaryOp.GT || bop == BinaryOp.GE) {
+ IRValue lv = this.lowerExpr(expr.left);
+ IRValue rv = this.lowerExpr(expr.right);
+ this.emit(IRInstr.cmp(lv, rv));
+ if (bop == BinaryOp.EQ) { return IROpcode.JNE; }
+ if (bop == BinaryOp.NE) { return IROpcode.JE; }
+ if (bop == BinaryOp.LT) { return IROpcode.JGE; }
+ if (bop == BinaryOp.LE) { return IROpcode.JG; }
+ if (bop == BinaryOp.GT) { return IROpcode.JLE; }
+ return IROpcode.JL;
+ }
+ }
+ IRValue cv = this.lowerExpr(expr);
+ this.emit(IRInstr.cmp(cv, IRValue.ofInt(0, IRType.I64)));
+ return IROpcode.JE;
+ }
+
+ // ===== Statement lowering =====
+
+ private needsReturnRetain(expr: Expr) : Boolean {
+ if (expr.kind == ExprKind.IDENT) { return true; }
+ if (expr.kind == ExprKind.FIELD) { return true; }
+ // TODO: extend to INDEX/DEREF when indexed/deref field access is lowered
+ return false;
+ }
+
+ private lowerStmt(stmt: Stmt) {
+ if (stmt.kind == StmtKind.RETURN) {
+ if (stmt.expr.kind != ExprKind.NULL_LIT) {
+ IRValue rv = this.lowerExpr(stmt.expr);
+ // ARC: retain ref-type return value before scope release
+ if (this.needsReturnRetain(stmt.expr)) {
+ String retCls = this.inferClass(stmt.expr);
+ Integer retClsIdx = this.classIdxOf(retCls);
+ if (retClsIdx >= 0) { this.emitRetain(rv, retClsIdx); }
+ }
+ // Full scope unwind
+ this.emitUnwindToDepth(0);
+ this.emit(IRInstr.ret(rv));
+ } else {
+ this.emitUnwindToDepth(0);
+ this.emit(IRInstr.retVoid());
+ }
+ return;
+ }
+
+ if (stmt.kind == StmtKind.EXPR) {
+ this.lowerExpr(stmt.expr);
+ return;
+ }
+
+ if (stmt.kind == StmtKind.VAR_DECL) {
+ String reg = this.newReg();
+ IRValue dst = IRValue.reg(reg, IRType.I64);
+ String varCls = this.tyToClass(stmt.ty);
+ if (stmt.initExpr.kind != ExprKind.NULL_LIT) {
+ IRValue src = this.lowerExpr(stmt.initExpr);
+ // ARC: retain ref-type initializers (let c = a)
+ if (stmt.initExpr.kind == ExprKind.IDENT) {
+ String initCls = this.inferClass(stmt.initExpr);
+ Integer initClsIdx = this.classIdxOf(initCls);
+ if (initClsIdx >= 0) { this.emitRetain(src, initClsIdx); }
+ }
+ this.emit(IRInstr.mov(dst, src));
+ if (varCls == "") { varCls = this.inferClass(stmt.initExpr); }
+ } else {
+ this.emit(IRInstr.mov(dst, IRValue.ofInt(0, IRType.I64)));
+ }
+ this.varDefine(stmt.name, reg);
+ if (varCls != "") { this.varSetClass(stmt.name, varCls); }
+ return;
+ }
+
+ if (stmt.kind == StmtKind.IF) {
+ String endL = this.newLabel("if_end");
+ String nextL = this.newLabel("if_else");
+ Integer jmpOp = this.lowerCond(stmt.cond);
+ this.emit(IRInstr.branch(jmpOp, nextL));
+ this.lowerBody(stmt.thenBody);
+ this.emit(IRInstr.jmp(endL));
+ this.emit(IRInstr.label(nextL));
+ Integer ei = 0;
+ while (ei < stmt.elseIfs.length()) {
+ ElseIfBranch eib = stmt.elseIfs.get(ei) as ElseIfBranch;
+ String afterL = this.newLabel("if_else");
+ Integer jopEI = this.lowerCond(eib.cond);
+ this.emit(IRInstr.branch(jopEI, afterL));
+ this.lowerBody(eib.body);
+ this.emit(IRInstr.jmp(endL));
+ this.emit(IRInstr.label(afterL));
+ ei = ei + 1;
+ }
+ if (stmt.elseBody != null && stmt.elseBody.length() > 0) {
+ this.lowerBody(stmt.elseBody);
+ }
+ this.emit(IRInstr.label(endL));
+ return;
+ }
+
+ if (stmt.kind == StmtKind.WHILE) {
+ String loopL = this.newLabel("while_loop");
+ String breakL = this.newLabel("while_brk");
+ String savedBreak = this.breakLbl;
+ String savedCont = this.contLbl;
+ Integer savedBreakDepth = this.breakDepth;
+ Integer savedContDepth = this.contDepth;
+ this.breakLbl = breakL;
+ this.contLbl = loopL;
+ this.breakDepth = this.scopeStack.length();
+ this.contDepth = this.scopeStack.length();
+ this.emit(IRInstr.label(loopL));
+ Integer jmpOp = this.lowerCond(stmt.cond);
+ this.emit(IRInstr.branch(jmpOp, breakL));
+ this.lowerBody(stmt.body);
+ this.emit(IRInstr.jmp(loopL));
+ this.emit(IRInstr.label(breakL));
+ this.breakLbl = savedBreak;
+ this.contLbl = savedCont;
+ this.breakDepth = savedBreakDepth;
+ this.contDepth = savedContDepth;
+ return;
+ }
+
+ if (stmt.kind == StmtKind.FOR) {
+ if (stmt.forInit != null) { this.lowerStmt(stmt.forInit); }
+ String loopL = this.newLabel("for_loop");
+ String breakL = this.newLabel("for_brk");
+ String contL = this.newLabel("for_upd");
+ String savedBreak = this.breakLbl;
+ String savedCont = this.contLbl;
+ Integer savedBreakDepth = this.breakDepth;
+ Integer savedContDepth = this.contDepth;
+ this.breakLbl = breakL;
+ this.contLbl = contL;
+ this.breakDepth = this.scopeStack.length();
+ this.contDepth = this.scopeStack.length();
+ this.emit(IRInstr.label(loopL));
+ if (stmt.forCond != null) {
+ Integer jop = this.lowerCond(stmt.forCond);
+ this.emit(IRInstr.branch(jop, breakL));
+ }
+ this.lowerBody(stmt.forBody);
+ this.emit(IRInstr.label(contL));
+ if (stmt.forStep != null) { this.lowerExpr(stmt.forStep); }
+ this.emit(IRInstr.jmp(loopL));
+ this.emit(IRInstr.label(breakL));
+ this.breakLbl = savedBreak;
+ this.contLbl = savedCont;
+ this.breakDepth = savedBreakDepth;
+ this.contDepth = savedContDepth;
+ return;
+ }
+
+ if (stmt.kind == StmtKind.FOR_EACH) {
+ IRValue iterVal = this.lowerExpr(stmt.iterable);
+ String iReg = this.newReg();
+ IRValue iDst = IRValue.reg(iReg, IRType.I64);
+ this.emit(IRInstr.mov(iDst, IRValue.ofInt(0, IRType.I64)));
+ this.varDefine("__fe_i_${iReg}", iReg);
+ String loopL = this.newLabel("fe_loop");
+ String breakL = this.newLabel("fe_brk");
+ String savedBreak = this.breakLbl;
+ String savedCont = this.contLbl;
+ Integer savedBreakDepth = this.breakDepth;
+ Integer savedContDepth = this.contDepth;
+ this.breakLbl = breakL;
+ this.contLbl = loopL;
+ this.breakDepth = this.scopeStack.length();
+ this.contDepth = this.scopeStack.length();
+ this.emit(IRInstr.label(loopL));
+ String lenReg = this.newReg();
+ IRValue lenDst = IRValue.reg(lenReg, IRType.I64);
+ List lenArgs = List();
+ lenArgs.append(iterVal);
+ this.emit(IRInstr.call(lenDst, "__arimo_list_length", lenArgs));
+ this.emit(IRInstr.cmp(iDst, lenDst));
+ this.emit(IRInstr.branch(IROpcode.JGE, breakL));
+ String itemReg = this.newReg();
+ IRValue itemDst = IRValue.reg(itemReg, IRType.I64);
+ List getArgs = List();
+ getArgs.append(iterVal);
+ getArgs.append(iDst);
+ this.emit(IRInstr.call(itemDst, "__arimo_list_get", getArgs));
+ this.varDefine(stmt.iterName, itemReg);
+ String iterCls = this.tyToClass(stmt.iterTy);
+ if (iterCls != "") { this.varSetClass(stmt.iterName, iterCls); }
+ this.lowerBody(stmt.body);
+ this.emit(IRInstr.binop(IROpcode.ADD, iDst, iDst, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp(loopL));
+ this.emit(IRInstr.label(breakL));
+ this.breakLbl = savedBreak;
+ this.contLbl = savedCont;
+ this.breakDepth = savedBreakDepth;
+ this.contDepth = savedContDepth;
+ return;
+ }
+
+ if (stmt.kind == StmtKind.BREAK) {
+ this.emitUnwindToDepth(this.breakDepth);
+ this.emit(IRInstr.jmp(this.breakLbl));
+ return;
+ }
+ if (stmt.kind == StmtKind.CONTINUE) {
+ this.emitUnwindToDepth(this.contDepth);
+ this.emit(IRInstr.jmp(this.contLbl));
+ return;
+ }
+ if (stmt.kind == StmtKind.BLOCK) { this.pushScope(); this.lowerBody(stmt.body); this.popScope(); return; }
+ if (stmt.kind == StmtKind.THROW) { this.lowerExpr(stmt.expr); return; }
+ if (stmt.kind == StmtKind.TRY) { this.lowerBody(stmt.tryBody); return; }
+ }
+
+ private lowerBody(stmts: List) {
+ Integer i = 0;
+ while (i < stmts.length()) {
+ Stmt s = stmts.get(i) as Stmt;
+ this.lowerStmt(s);
+ i = i + 1;
+ }
+ }
+
+ // ===== Expression lowering =====
+
+ private lowerExpr(expr: Expr) : IRValue {
+ if (expr.kind == ExprKind.INT_LIT) { return IRValue.ofInt(expr.intVal, IRType.I64); }
+ if (expr.kind == ExprKind.CHAR_LIT) { return IRValue.ofInt(expr.intVal, IRType.I64); }
+
+ if (expr.kind == ExprKind.BOOL_LIT) {
+ if (expr.boolVal) { return IRValue.ofInt(1, IRType.I64); }
+ return IRValue.ofInt(0, IRType.I64);
+ }
+
+ if (expr.kind == ExprKind.NULL_LIT) { return IRValue.ofInt(0, IRType.I64); }
+
+ if (expr.kind == ExprKind.STR_LIT) {
+ String name = this.internStr(expr.strVal);
+ return IRValue.global(name);
+ }
+
+ if (expr.kind == ExprKind.IDENT) {
+ String reg = this.varLookup(expr.strVal);
+ if (reg != "") { return IRValue.reg(reg, IRType.I64); }
+ return IRValue.ofInt(0, IRType.I64);
+ }
+
+ if (expr.kind == ExprKind.THIS) {
+ if (this.thisReg != "") { return IRValue.reg(this.thisReg, IRType.PTR); }
+ return IRValue.ofInt(0, IRType.I64);
+ }
+
+ if (expr.kind == ExprKind.FIELD) {
+ IRValue objVal = this.lowerExpr(expr.object);
+ String objCls = this.inferClass(expr.object);
+ Integer clsIdx = this.classIdxOf(objCls);
+ if (clsIdx >= 0) {
+ Integer fldIdx = this.fieldIdxOf(clsIdx, expr.field);
+ if (fldIdx >= 0) { return this.emitFieldLoad(objVal, fldIdx * 8); }
+ }
+ return this.emitFieldLoad(objVal, 0);
+ }
+
+ if (expr.kind == ExprKind.NULL_SAFE) {
+ if (expr.args == null || expr.args.length() == 0) {
+ IRValue objVal = this.lowerExpr(expr.object);
+ String objCls = this.inferClass(expr.object);
+ Integer clsIdx = this.classIdxOf(objCls);
+ if (clsIdx >= 0) {
+ Integer fldIdx = this.fieldIdxOf(clsIdx, expr.field);
+ if (fldIdx >= 0) { return this.emitFieldLoad(objVal, fldIdx * 8); }
+ }
+ return this.emitFieldLoad(objVal, 0);
+ }
+ return this.lowerMethodCall(expr.object, expr.field, expr.args);
+ }
+
+ if (expr.kind == ExprKind.CTOR) {
+ return this.lowerCtor(expr.class_, expr.args);
+ }
+
+ if (expr.kind == ExprKind.STATIC_CALL) {
+ return this.lowerStaticCall(expr.class_, expr.method, expr.args);
+ }
+
+ if (expr.kind == ExprKind.METHOD) {
+ return this.lowerMethodCall(expr.object, expr.method, expr.args);
+ }
+
+ if (expr.kind == ExprKind.BINOP) {
+ return this.lowerBinop(expr);
+ }
+
+ if (expr.kind == ExprKind.UNARY) {
+ IRValue operand = this.lowerExpr(expr.operand);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ if (expr.op == 1) { this.emit(IRInstr.unop(IROpcode.NEG, dst, operand)); }
+ else if (expr.op == 2) { this.emit(IRInstr.unop(IROpcode.NOT, dst, operand)); }
+ else if (expr.op == 4 || expr.op == 5 || expr.op == 6 || expr.op == 7) {
+ if (expr.operand.kind == ExprKind.IDENT) {
+ String varName = expr.operand.strVal;
+ String oldReg = this.varLookup(varName);
+ if (oldReg != "") {
+ IRValue inPlace = IRValue.reg(oldReg, IRType.I64);
+ if (expr.op == 6 || expr.op == 7) {
+ // POST: copy old value to dst, then update oldReg in-place
+ this.emit(IRInstr.mov(dst, inPlace));
+ if (expr.op == 6) { this.emit(IRInstr.binop(IROpcode.ADD, inPlace, inPlace, IRValue.ofInt(1, IRType.I64))); }
+ else { this.emit(IRInstr.binop(IROpcode.SUB, inPlace, inPlace, IRValue.ofInt(1, IRType.I64))); }
+ } else {
+ // PRE: update oldReg in-place, return it (now holds new value)
+ if (expr.op == 4) { this.emit(IRInstr.binop(IROpcode.ADD, inPlace, inPlace, IRValue.ofInt(1, IRType.I64))); }
+ else { this.emit(IRInstr.binop(IROpcode.SUB, inPlace, inPlace, IRValue.ofInt(1, IRType.I64))); }
+ return inPlace;
+ }
+ } else {
+ this.emit(IRInstr.mov(dst, operand));
+ }
+ } else if (expr.operand.kind == ExprKind.FIELD) {
+ String fldName = expr.operand.field; Expr objExpr = expr.operand.object;
+ String objCls = this.inferClass(objExpr); Integer clsIdx = this.classIdxOf(objCls);
+ if (clsIdx >= 0) {
+ Integer fldIdx = this.fieldIdxOf(clsIdx, fldName);
+ if (fldIdx >= 0) {
+ Integer fldOff = fldIdx * 8;
+ IRValue objPtr = this.lowerExpr(objExpr);
+ IRValue fldPtr = this.emitFieldPtr(objPtr, fldOff);
+ String oldReg = this.newReg(); IRValue oldVal = IRValue.reg(oldReg, IRType.I64);
+ this.emit(IRInstr.load(oldVal, fldPtr, IRType.I64));
+ if (expr.op == 6 || expr.op == 7) {
+ this.emit(IRInstr.mov(dst, oldVal));
+ String newReg = this.newReg(); IRValue newVal = IRValue.reg(newReg, IRType.I64);
+ if (expr.op == 6) { this.emit(IRInstr.binop(IROpcode.ADD, newVal, oldVal, IRValue.ofInt(1, IRType.I64))); }
+ else { this.emit(IRInstr.binop(IROpcode.SUB, newVal, oldVal, IRValue.ofInt(1, IRType.I64))); }
+ this.emit(IRInstr.store(newVal, fldPtr, IRType.I64));
+ } else {
+ String newReg = this.newReg(); IRValue newVal = IRValue.reg(newReg, IRType.I64);
+ if (expr.op == 4) { this.emit(IRInstr.binop(IROpcode.ADD, newVal, oldVal, IRValue.ofInt(1, IRType.I64))); }
+ else { this.emit(IRInstr.binop(IROpcode.SUB, newVal, oldVal, IRValue.ofInt(1, IRType.I64))); }
+ this.emit(IRInstr.store(newVal, fldPtr, IRType.I64));
+ this.emit(IRInstr.mov(dst, newVal));
+ }
+ } else { this.emit(IRInstr.mov(dst, operand)); }
+ } else { this.emit(IRInstr.mov(dst, operand)); }
+ } else {
+ this.emit(IRInstr.mov(dst, operand));
+ }
+ }
+ else { this.emit(IRInstr.mov(dst, operand)); }
+ return dst;
+ }
+
+ if (expr.kind == ExprKind.CAST) {
+ IRValue src = this.lowerExpr(expr.operand);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ this.emit(IRInstr.mov(dst, src));
+ return dst;
+ }
+
+ if (expr.kind == ExprKind.TERNARY) {
+ String falseL = this.newLabel("tern_f");
+ String endL = this.newLabel("tern_e");
+ String resReg = this.newReg();
+ IRValue resDst = IRValue.reg(resReg, IRType.I64);
+ Integer jop = this.lowerCond(expr.cond);
+ this.emit(IRInstr.branch(jop, falseL));
+ IRValue tv = this.lowerExpr(expr.then_);
+ this.emit(IRInstr.mov(resDst, tv));
+ this.emit(IRInstr.jmp(endL));
+ this.emit(IRInstr.label(falseL));
+ IRValue fv = this.lowerExpr(expr.else_);
+ this.emit(IRInstr.mov(resDst, fv));
+ this.emit(IRInstr.label(endL));
+ return resDst;
+ }
+
+ if (expr.kind == ExprKind.NULL_COALESCE) {
+ String notNullL = this.newLabel("nn_ok");
+ String endL = this.newLabel("nn_end");
+ String resReg = this.newReg();
+ IRValue resDst = IRValue.reg(resReg, IRType.I64);
+ IRValue lv = this.lowerExpr(expr.left);
+ this.emit(IRInstr.mov(resDst, lv));
+ this.emit(IRInstr.cmp(lv, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JNE, notNullL));
+ IRValue rv = this.lowerExpr(expr.right);
+ this.emit(IRInstr.mov(resDst, rv));
+ this.emit(IRInstr.jmp(endL));
+ this.emit(IRInstr.label(notNullL));
+ this.emit(IRInstr.label(endL));
+ return resDst;
+ }
+
+ if (expr.kind == ExprKind.STR_INTERP) {
+ String emptyName = this.internStr("");
+ IRValue result = IRValue.global(emptyName);
+ Integer pi = 0;
+ while (pi < expr.parts.length()) {
+ StrPart part = expr.parts.get(pi) as StrPart;
+ IRValue partVal = IRValue.ofInt(0, IRType.I64);
+ if (part.isLit) {
+ String sn = this.internStr(part.lit);
+ partVal = IRValue.global(sn);
+ } else {
+ IRValue rawVal = this.lowerExpr(part.expr);
+ if (this.isIntegerExpr(part.expr)) {
+ String toStrReg = this.newReg();
+ IRValue toStrDst = IRValue.reg(toStrReg, IRType.PTR);
+ List tsArgs = List();
+ tsArgs.append(rawVal);
+ this.emit(IRInstr.call(toStrDst, "__arimo_i64_to_str", tsArgs));
+ partVal = toStrDst;
+ } else {
+ partVal = rawVal;
+ }
+ }
+ String catReg = this.newReg();
+ IRValue catDst = IRValue.reg(catReg, IRType.PTR);
+ List catArgs = List();
+ catArgs.append(result);
+ catArgs.append(partVal);
+ this.emit(IRInstr.call(catDst, "__arimo_strcat", catArgs));
+ result = catDst;
+ pi = pi + 1;
+ }
+ return result;
+ }
+
+ return IRValue.ofInt(0, IRType.I64);
+ }
+
+ // ===== Binary op lowering =====
+
+ private lowerBinop(expr: Expr) : IRValue {
+ Integer bop = expr.op;
+
+ if (bop == BinaryOp.ASSIGN) {
+ if (expr.left.kind == ExprKind.IDENT) {
+ String vReg = this.varLookup(expr.left.strVal);
+ if (vReg != "") {
+ IRValue rv = this.lowerExpr(expr.right);
+ // ARC: retain new value if ref-type variable
+ if (expr.right.kind == ExprKind.IDENT) {
+ String rhsCls = this.inferClass(expr.right);
+ Integer rhsClsIdx = this.classIdxOf(rhsCls);
+ if (rhsClsIdx >= 0) { this.emitRetain(rv, rhsClsIdx); }
+ }
+ // ARC: release old value before overwrite
+ String oldCls = this.varClassOf(expr.left.strVal);
+ Integer oldClsIdx = this.classIdxOf(oldCls);
+ if (oldClsIdx >= 0) {
+ this.emitRelease(IRValue.reg(vReg, IRType.I64), oldClsIdx);
+ }
+ IRValue dst = IRValue.reg(vReg, IRType.I64);
+ this.emit(IRInstr.mov(dst, rv));
+ return dst;
+ }
+ }
+ if (expr.left.kind == ExprKind.FIELD) {
+ String objCls = this.inferClass(expr.left.object);
+ Integer clsIdx = this.classIdxOf(objCls);
+ IRValue objVal = this.lowerExpr(expr.left.object);
+ Integer fldOff = 0;
+ Integer fldIdx = -1;
+ if (clsIdx >= 0) {
+ fldIdx = this.fieldIdxOf(clsIdx, expr.left.field);
+ if (fldIdx >= 0) { fldOff = fldIdx * 8; }
+ }
+ IRValue rv2 = this.lowerExpr(expr.right);
+ // ARC: retain new value if ref-type variable (mirrors IDENT case)
+ if (expr.right.kind == ExprKind.IDENT) {
+ String rhsCls = this.inferClass(expr.right);
+ Integer rhsClsIdx = this.classIdxOf(rhsCls);
+ if (rhsClsIdx >= 0) { this.emitRetain(rv2, rhsClsIdx); }
+ }
+ // Compute field pointer ONCE (avoid double-call reg-clobber)
+ IRValue fldPtr = this.emitFieldPtr(objVal, fldOff);
+ // ARC: release old field value before overwrite
+ if (clsIdx >= 0 && fldIdx >= 0) {
+ Integer start = this.classFldStarts.get(clsIdx) as Integer;
+ String oldFldCls = this.allFieldClasses.get(start + fldIdx - 1) as String;
+ Integer oldFldClsIdx = this.classIdxOf(oldFldCls);
+ if (oldFldClsIdx >= 0) {
+ String oldReg = this.newReg();
+ this.emit(IRInstr.load(IRValue.reg(oldReg, IRType.I64), fldPtr, IRType.I64));
+ this.emitRelease(IRValue.reg(oldReg, IRType.I64), oldFldClsIdx);
+ }
+ }
+ this.emit(IRInstr.store(rv2, fldPtr, IRType.I64));
+ return IRValue.ofInt(0, IRType.I64);
+ }
+ IRValue rv3 = this.lowerExpr(expr.right);
+ return rv3;
+ }
+
+ if (bop == BinaryOp.AND || bop == BinaryOp.OR) {
+ IRValue lv = this.lowerExpr(expr.left);
+ IRValue rv = this.lowerExpr(expr.right);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ if (bop == BinaryOp.AND) { this.emit(IRInstr.binop(IROpcode.AND, dst, lv, rv)); }
+ else { this.emit(IRInstr.binop(IROpcode.OR, dst, lv, rv)); }
+ return dst;
+ }
+
+ if (bop == BinaryOp.EQ || bop == BinaryOp.NE ||
+ bop == BinaryOp.LT || bop == BinaryOp.LE ||
+ bop == BinaryOp.GT || bop == BinaryOp.GE) {
+ IRValue lv = this.lowerExpr(expr.left);
+ IRValue rv = this.lowerExpr(expr.right);
+ String trueL = this.newLabel("cmp_t");
+ String endL = this.newLabel("cmp_e");
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ this.emit(IRInstr.cmp(lv, rv));
+ Integer trueJmp = IROpcode.JE;
+ if (bop == BinaryOp.EQ) { trueJmp = IROpcode.JE; }
+ if (bop == BinaryOp.NE) { trueJmp = IROpcode.JNE; }
+ if (bop == BinaryOp.LT) { trueJmp = IROpcode.JL; }
+ if (bop == BinaryOp.LE) { trueJmp = IROpcode.JLE; }
+ if (bop == BinaryOp.GT) { trueJmp = IROpcode.JG; }
+ if (bop == BinaryOp.GE) { trueJmp = IROpcode.JGE; }
+ this.emit(IRInstr.branch(trueJmp, trueL));
+ this.emit(IRInstr.mov(dst, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.jmp(endL));
+ this.emit(IRInstr.label(trueL));
+ this.emit(IRInstr.mov(dst, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.label(endL));
+ return dst;
+ }
+
+ Integer irop = IROpcode.ADD;
+ if (bop == BinaryOp.ADD) { irop = IROpcode.ADD; }
+ if (bop == BinaryOp.SUB) { irop = IROpcode.SUB; }
+ if (bop == BinaryOp.MUL) { irop = IROpcode.MUL; }
+ if (bop == BinaryOp.DIV) { irop = IROpcode.DIV; }
+ if (bop == BinaryOp.MOD) { irop = IROpcode.MOD; }
+ if (bop == BinaryOp.BITAND) { irop = IROpcode.AND; }
+ if (bop == BinaryOp.BITOR) { irop = IROpcode.OR; }
+ if (bop == BinaryOp.XOR) { irop = IROpcode.XOR; }
+ if (bop == BinaryOp.SHL) { irop = IROpcode.SHL; }
+ if (bop == BinaryOp.SHR) { irop = IROpcode.SHR; }
+
+ IRValue lv2 = this.lowerExpr(expr.left);
+ IRValue rv2 = this.lowerExpr(expr.right);
+ String dr2 = this.newReg();
+ IRValue dst2 = IRValue.reg(dr2, IRType.I64);
+ this.emit(IRInstr.binop(irop, dst2, lv2, rv2));
+ return dst2;
+ }
+
+ // ===== Static call lowering =====
+
+ private lowerStaticCall(class_: String, method: String, args: List) : IRValue {
+ if (class_ == "IO" && method == "println") {
+ Expr argExpr = args.get(0) as Expr;
+ Boolean isStr = this.isStringExpr(argExpr);
+ if (argExpr.kind == ExprKind.METHOD) {
+ if (argExpr.object.kind == ExprKind.IDENT && argExpr.object.strVal == "Env") { isStr = true; }
+ }
+ if (argExpr.kind == ExprKind.STATIC_CALL) {
+ if (argExpr.class_ == "Env") { isStr = true; }
+ }
+ if (isStr) {
+ IRValue strArg = this.lowerExpr(argExpr);
+ List callArgs = List();
+ callArgs.append(strArg);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.VOID);
+ this.emit(IRInstr.call(dst, "__arimo_println", callArgs));
+ } else {
+ IRValue rawVal = this.lowerExpr(argExpr);
+ List intArgs = List();
+ intArgs.append(rawVal);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.VOID);
+ this.emit(IRInstr.call(dst, "__arimo_println_int", intArgs));
+ }
+ return IRValue.none();
+ }
+ if (class_ == "IO" && method == "print") {
+ Expr argExpr2 = args.get(0) as Expr;
+ Boolean isStr2 = this.isStringExpr(argExpr2);
+ if (argExpr2.kind == ExprKind.METHOD) {
+ if (argExpr2.object.kind == ExprKind.IDENT && argExpr2.object.strVal == "Env") { isStr2 = true; }
+ }
+ if (isStr2) {
+ IRValue strArg2 = this.lowerExpr(argExpr2);
+ List callArgs2 = List();
+ callArgs2.append(strArg2);
+ String dr2 = this.newReg();
+ IRValue dst2 = IRValue.reg(dr2, IRType.VOID);
+ this.emit(IRInstr.call(dst2, "__arimo_print", callArgs2));
+ } else {
+ IRValue rawVal2 = this.lowerExpr(argExpr2);
+ List intArgs2 = List();
+ intArgs2.append(rawVal2);
+ String dr2 = this.newReg();
+ IRValue dst2 = IRValue.reg(dr2, IRType.VOID);
+ this.emit(IRInstr.call(dst2, "__arimo_print_int", intArgs2));
+ }
+ return IRValue.none();
+ }
+
+ if (class_ == "IO" && method == "error") {
+ // IO.error(msg) → print to stderr via write syscall
+ IRValue errArg = this.lowerExpr(args.get(0) as Expr);
+ this.emit(IRInstr.call(IRValue.reg(this.newReg(), IRType.VOID), "__arimo_print", List()));
+ // Also print the message: reuse print helper with stderr fd=2
+ String plenR = this.newReg();
+ IRValue plenDst = IRValue.reg(plenR, IRType.I64);
+ List lenA = List(); lenA.append(errArg);
+ this.emit(IRInstr.call(plenDst, "__arimo_strlen", lenA));
+ List wrA = List();
+ wrA.append(IRValue.ofInt(2, IRType.I64));
+ wrA.append(errArg);
+ wrA.append(IRValue.reg(plenR, IRType.I64));
+ this.emit(IRInstr.syscall(IRValue.reg(this.newReg(), IRType.I64), 1, wrA));
+ // newline
+ List nlA = List();
+ nlA.append(IRValue.ofInt(2, IRType.I64));
+ nlA.append(IRValue.global(this.internStr("\n")));
+ nlA.append(IRValue.ofInt(1, IRType.I64));
+ this.emit(IRInstr.syscall(IRValue.reg(this.newReg(), IRType.I64), 1, nlA));
+ return IRValue.none();
+ }
+
+ // Env built-ins
+ if (class_ == "Env" && method == "platform") {
+ String platform = "linux";
+ if (!this.linux) { platform = "windows"; }
+ return IRValue.global(this.internStr(platform));
+ }
+ if (class_ == "Env" && method == "exit") {
+ Integer code = 0;
+ if (args.length() > 0) {
+ Expr e = args.get(0) as Expr;
+ if (e.kind == ExprKind.INT_LIT) { code = e.intVal; }
+ }
+ if (this.linux) {
+ List scArgs = List();
+ scArgs.append(IRValue.ofInt(code, IRType.I64));
+ this.emit(IRInstr.syscall(IRValue.none(), 60, scArgs));
+ } else {
+ List ea = List();
+ ea.append(IRValue.ofInt(code, IRType.I64));
+ String er = this.newReg();
+ this.emit(IRInstr.call(IRValue.reg(er, IRType.VOID), "__ext__ExitProcess", ea));
+ }
+ return IRValue.none();
+ }
+ if (class_ == "Env" && method == "args") {
+ String argcReg = this.newReg();
+ this.emit(IRInstr.load(IRValue.reg(argcReg, IRType.I64), IRValue.global("__arimo_argc"), IRType.I64));
+ String argvReg = this.newReg();
+ this.emit(IRInstr.load(IRValue.reg(argvReg, IRType.PTR), IRValue.global("__arimo_argv"), IRType.PTR));
+ List ba = List(); ba.append(IRValue.reg(argcReg, IRType.I64)); ba.append(IRValue.reg(argvReg, IRType.PTR));
+ String lr = this.newReg(); this.emit(IRInstr.call(IRValue.reg(lr, IRType.PTR), "__arimo_build_args", ba));
+ return IRValue.reg(lr, IRType.PTR);
+ }
+ if (class_ == "Env" && method == "exePath") {
+ String avReg = this.newReg();
+ this.emit(IRInstr.load(IRValue.reg(avReg, IRType.PTR), IRValue.global("__arimo_argv"), IRType.PTR));
+ String a0Reg = this.newReg();
+ this.emit(IRInstr.load(IRValue.reg(a0Reg, IRType.PTR), IRValue.reg(avReg, IRType.PTR), IRType.PTR));
+ return IRValue.reg(a0Reg, IRType.PTR);
+ }
+ // Generic static call fallback
+ String fnName = "${class_}__${method}";
+ List callArgsFn = List();
+ Integer ai = 0;
+ while (ai < args.length()) {
+ Expr arg = args.get(ai) as Expr;
+ callArgsFn.append(this.lowerExpr(arg));
+ ai = ai + 1;
+ }
+ String dr3 = this.newReg();
+ IRValue dst3 = IRValue.reg(dr3, IRType.I64);
+ this.emit(IRInstr.call(dst3, fnName, callArgsFn));
+ return dst3;
+ }
+
+ // ===== Constructor lowering =====
+
+ private lowerLinuxLibcCall(name: String, args: List) : IRValue {
+ // fopen(path, mode) → call shared __arimo_fopen helper
+ if (name == "fopen") {
+ IRValue pathVal = this.lowerExpr(args.get(0) as Expr);
+ IRValue modeVal = this.lowerExpr(args.get(1) as Expr);
+ String retR = this.newReg(); IRValue ret = IRValue.reg(retR, IRType.I64);
+ List fargs = List(); fargs.append(pathVal); fargs.append(modeVal);
+ this.emit(IRInstr.call(ret, "__arimo_fopen", fargs));
+ return ret;
+ }
+ // fread(buf, size, n, fd) → read(fd, buf, size*n) syscall 0
+ if (name == "fread") {
+ IRValue bufVal = this.lowerExpr(args.get(0) as Expr);
+ IRValue sizeVal = this.lowerExpr(args.get(1) as Expr);
+ IRValue nVal = this.lowerExpr(args.get(2) as Expr);
+ IRValue fdVal = this.lowerExpr(args.get(3) as Expr);
+ String totR = this.newReg();
+ IRValue total = IRValue.reg(totR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, total, sizeVal, nVal));
+ String resR = this.newReg();
+ IRValue result = IRValue.reg(resR, IRType.I64);
+ List rdArgs = List();
+ rdArgs.append(fdVal);
+ rdArgs.append(bufVal);
+ rdArgs.append(total);
+ this.emit(IRInstr.syscall(result, 0, rdArgs));
+ return result;
+ }
+ // fwrite(buf, size, n, fd) → write(fd, buf, size*n) syscall 1
+ if (name == "fwrite") {
+ IRValue bufVal = this.lowerExpr(args.get(0) as Expr);
+ IRValue sizeVal = this.lowerExpr(args.get(1) as Expr);
+ IRValue nVal = this.lowerExpr(args.get(2) as Expr);
+ IRValue fdVal = this.lowerExpr(args.get(3) as Expr);
+ String totR = this.newReg();
+ IRValue total = IRValue.reg(totR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, total, sizeVal, nVal));
+ String resR = this.newReg();
+ IRValue result = IRValue.reg(resR, IRType.I64);
+ List wrArgs = List();
+ wrArgs.append(fdVal);
+ wrArgs.append(bufVal);
+ wrArgs.append(total);
+ this.emit(IRInstr.syscall(result, 1, wrArgs));
+ return result;
+ }
+ // fclose(fd) → close(fd) syscall 3
+ if (name == "fclose") {
+ IRValue fdVal = this.lowerExpr(args.get(0) as Expr);
+ String clR = this.newReg();
+ IRValue clDst = IRValue.reg(clR, IRType.I64);
+ List clArgs = List();
+ clArgs.append(fdVal);
+ this.emit(IRInstr.syscall(clDst, 3, clArgs));
+ String zR = this.newReg();
+ IRValue zero = IRValue.reg(zR, IRType.I64);
+ this.emit(IRInstr.mov(zero, IRValue.ofInt(0, IRType.I64)));
+ return zero;
+ }
+ // fseek(fd, offset, whence) → lseek(fd, offset, whence) syscall 8
+ if (name == "fseek") {
+ IRValue fdVal = this.lowerExpr(args.get(0) as Expr);
+ IRValue offsetVal = this.lowerExpr(args.get(1) as Expr);
+ IRValue whenceVal = this.lowerExpr(args.get(2) as Expr);
+ String skR = this.newReg();
+ IRValue skDst = IRValue.reg(skR, IRType.I64);
+ List skArgs = List();
+ skArgs.append(fdVal);
+ skArgs.append(offsetVal);
+ skArgs.append(whenceVal);
+ this.emit(IRInstr.syscall(skDst, 8, skArgs));
+ String zR = this.newReg();
+ IRValue zero = IRValue.reg(zR, IRType.I64);
+ this.emit(IRInstr.mov(zero, IRValue.ofInt(0, IRType.I64)));
+ return zero;
+ }
+ // ftell(fd) → lseek(fd, 0, SEEK_CUR=1) syscall 8
+ if (name == "ftell") {
+ IRValue fdVal = this.lowerExpr(args.get(0) as Expr);
+ String posR = this.newReg();
+ IRValue pos = IRValue.reg(posR, IRType.I64);
+ List ftArgs = List();
+ ftArgs.append(fdVal);
+ ftArgs.append(IRValue.ofInt(0, IRType.I64));
+ ftArgs.append(IRValue.ofInt(1, IRType.I64));
+ this.emit(IRInstr.syscall(pos, 8, ftArgs));
+ return pos;
+ }
+ // remove(path) → unlink(path) syscall 87
+ if (name == "remove") {
+ IRValue pathVal = this.lowerExpr(args.get(0) as Expr);
+ String rmR = this.newReg();
+ IRValue rmDst = IRValue.reg(rmR, IRType.I64);
+ List rmArgs = List();
+ rmArgs.append(pathVal);
+ this.emit(IRInstr.syscall(rmDst, 87, rmArgs));
+ return rmDst;
+ }
+ // calloc(n, size) → mmap(NULL, n*size, PROT_RW=3, MAP_PRIVATE|ANON=34, -1, 0) syscall 9
+ if (name == "calloc") {
+ IRValue nVal = this.lowerExpr(args.get(0) as Expr);
+ IRValue sizeVal = this.lowerExpr(args.get(1) as Expr);
+ String totR = this.newReg();
+ IRValue total = IRValue.reg(totR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, total, nVal, sizeVal));
+ String ptrR = this.newReg();
+ IRValue ptr = IRValue.reg(ptrR, IRType.PTR);
+ List mmArgs = List();
+ mmArgs.append(IRValue.ofInt(0, IRType.I64));
+ mmArgs.append(total);
+ mmArgs.append(IRValue.ofInt(3, IRType.I64));
+ mmArgs.append(IRValue.ofInt(34, IRType.I64));
+ mmArgs.append(IRValue.ofInt(-1, IRType.I64));
+ mmArgs.append(IRValue.ofInt(0, IRType.I64));
+ this.emit(IRInstr.syscall(ptr, 9, mmArgs));
+ return ptr;
+ }
+ // fputc(c, fd) → stack-alloc 1 byte, store c, write(fd, buf, 1) syscall 1
+ if (name == "fputc") {
+ IRValue cVal = this.lowerExpr(args.get(0) as Expr);
+ IRValue fdVal = this.lowerExpr(args.get(1) as Expr);
+ String bufR = this.newReg();
+ IRValue buf = IRValue.reg(bufR, IRType.PTR);
+ this.emit(IRInstr.alloc(buf, 1));
+ this.emit(IRInstr.store(cVal, buf, IRType.I8));
+ String wR = this.newReg();
+ IRValue wDst = IRValue.reg(wR, IRType.I64);
+ List wArgs = List();
+ wArgs.append(fdVal);
+ wArgs.append(buf);
+ wArgs.append(IRValue.ofInt(1, IRType.I64));
+ this.emit(IRInstr.syscall(wDst, 1, wArgs));
+ return cVal;
+ }
+ // system(cmd) → __arimo_system helper (fork+execve+waitpid via syscalls)
+ if (name == "system") {
+ IRValue cmdVal = this.lowerExpr(args.get(0) as Expr);
+ String retR = this.newReg(); IRValue ret = IRValue.reg(retR, IRType.I64);
+ List sa = List(); sa.append(cmdVal);
+ this.emit(IRInstr.call(ret, "__arimo_system", sa));
+ return ret;
+ }
+ return IRValue.none();
+ }
+
+ private lowerCtor(class_: String, args: List) : IRValue {
+ if (class_ == "List") {
+ String lr = this.newReg();
+ IRValue lDst = IRValue.reg(lr, IRType.PTR);
+ List lArgs = List();
+ this.emit(IRInstr.call(lDst, "__arimo_list_new", lArgs));
+ return lDst;
+ }
+ // super(...) call inside constructor: resolve parent class, reuse `this` pointer.
+ // Does NOT allocate — object is already allocated by the derived class.
+ // Parser generates class_ = "__super__" (Expr.ctorCall("__super__", ...))
+ if (class_ == "__super__") {
+ Integer myIdx = this.classIdxOf(this.curClass);
+ Integer parentIdx = -1;
+ if (myIdx >= 0 && myIdx < this.classParents.length()) {
+ parentIdx = this.classParents.get(myIdx) as Integer;
+ }
+ if (parentIdx < 0) {
+ IO.println("arc: [FAIL] cannot resolve super constructor for ${this.curClass} — no parent class found");
+ return IRValue.ofInt(0, IRType.I64);
+ }
+ String parentClass = this.classNames.get(parentIdx) as String;
+ // `this` is a function parameter (not a varDeclare), use reg directly.
+ List superArgs = List();
+ superArgs.append(IRValue.reg("this", IRType.PTR));
+ Integer ai = 0;
+ while (ai < args.length()) {
+ Expr arg = args.get(ai) as Expr;
+ superArgs.append(this.lowerExpr(arg));
+ ai = ai + 1;
+ }
+ String supInitR = this.newReg();
+ IRValue supInitDst = IRValue.reg(supInitR, IRType.VOID);
+ this.emit(IRInstr.call(supInitDst, "${parentClass}__init", superArgs));
+ return IRValue.none();
+ }
+ if (this.linux) {
+ IRValue libcRes = this.lowerLinuxLibcCall(class_, args);
+ if (!libcRes.isNone()) { return libcRes; }
+ }
+ Integer clsIdx = this.classIdxOf(class_);
+ Integer sz = 16;
+ if (clsIdx >= 0) { sz = this.classSizeBytes(clsIdx); }
+ IRValue objPtr = this.emitHeapAlloc(IRValue.ofInt(sz, IRType.I64));
+ if (clsIdx >= 0) {
+ this.emitFieldStore(objPtr, this.refCountOff(clsIdx), IRValue.ofInt(1, IRType.I64));
+ }
+ List initArgs = List();
+ initArgs.append(objPtr);
+ Integer ai = 0;
+ while (ai < args.length()) {
+ Expr arg = args.get(ai) as Expr;
+ initArgs.append(this.lowerExpr(arg));
+ ai = ai + 1;
+ }
+ String initRes = this.newReg();
+ IRValue initDst = IRValue.reg(initRes, IRType.VOID);
+ this.emit(IRInstr.call(initDst, "${class_}__init", initArgs));
+ return objPtr;
+ }
+
+ // ===== Method call lowering =====
+
+ private lowerMethodCall(obj: Expr, method: String, args: List) : IRValue {
+ if (obj.kind == ExprKind.IDENT) {
+ if (obj.strVal == "IO") {
+ return this.lowerStaticCall("IO", method, args);
+ }
+ if (obj.strVal == "Env") {
+ return this.lowerStaticCall("Env", method, args);
+ }
+ // Static method call on known class: ClassName.method()
+ if (this.classIdxOf(obj.strVal) >= 0) {
+ return this.lowerStaticCall(obj.strVal, method, args);
+ }
+ }
+
+ IRValue objVal = this.lowerExpr(obj);
+ String objCls = this.inferClass(obj);
+
+ // List methods
+ if (objCls == "List") {
+ return this.lowerListMethod(objVal, method, args);
+ }
+
+ // toString() handler — must go before String/List dispatch to avoid
+ // lowerStringMethod/lowerListMethod fallback generating __str_toString
+ // which is never defined.
+ if (method == "toString") {
+ if (objCls == "String" || this.isStringExpr(obj)) { return IRValue.reg(objVal.name, objVal.ty); }
+ if (objCls == "" || objCls == "Integer") {
+ String tsReg = this.newReg();
+ IRValue tsDst = IRValue.reg(tsReg, IRType.PTR);
+ List tsArgs = List();
+ tsArgs.append(objVal);
+ this.emit(IRInstr.call(tsDst, "__arimo_i64_to_str", tsArgs));
+ return tsDst;
+ }
+ // Unknown type: fall through
+ }
+
+ // String methods
+ if (objCls == "String") {
+ return this.lowerStringMethod(objVal, method, args);
+ }
+
+ // length() on unknown type: prefer list if we can't tell
+ if (method == "length") {
+ if (objCls == "") {
+ String lr = this.newReg();
+ IRValue lDst = IRValue.reg(lr, IRType.I64);
+ List lArgs = List();
+ lArgs.append(objVal);
+ this.emit(IRInstr.call(lDst, "__arimo_strlen", lArgs));
+ return lDst;
+ }
+ }
+ // String method heuristic: call on unknown obj with string-like method names
+ if (method == "compareTo" || method == "concat" || method == "startsWith" ||
+ method == "endsWith" || method == "substring" || method == "indexOf" ||
+ method == "contains" || method == "charCodeAt"|| method == "charAt" ||
+ method == "isEmpty" || method == "equals" || method == "toUpperCase"||
+ method == "toLowerCase" || method == "toLower" || method == "toUpper" ||
+ method == "parseInt" || method == "parseFloat") {
+ return this.lowerStringMethod(objVal, method, args);
+ }
+
+ // User class method dispatch
+ if (objCls != "") {
+ List callArgsCls = List();
+ callArgsCls.append(objVal);
+ Integer ai = 0;
+ while (ai < args.length()) {
+ Expr arg = args.get(ai) as Expr;
+ callArgsCls.append(this.lowerExpr(arg));
+ ai = ai + 1;
+ }
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ this.emit(IRInstr.call(dst, "${objCls}__${method}", callArgsCls));
+ return dst;
+ }
+
+ // Fallback
+ List fallbackArgs = List();
+ fallbackArgs.append(objVal);
+ Integer ai2 = 0;
+ while (ai2 < args.length()) {
+ Expr arg2 = args.get(ai2) as Expr;
+ fallbackArgs.append(this.lowerExpr(arg2));
+ ai2 = ai2 + 1;
+ }
+ String dr4 = this.newReg();
+ IRValue dst4 = IRValue.reg(dr4, IRType.I64);
+ this.emit(IRInstr.call(dst4, "__method_${method}", fallbackArgs));
+ return dst4;
+ }
+
+ private lowerListMethod(objVal: IRValue, method: String, args: List) : IRValue {
+ if (method == "length" || method == "size") {
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ List a = List();
+ a.append(objVal);
+ this.emit(IRInstr.call(dst, "__arimo_list_length", a));
+ return dst;
+ }
+ if (method == "append" || method == "add") {
+ IRValue val = this.lowerExpr(args.get(0) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(val);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.VOID);
+ this.emit(IRInstr.call(dst, "__arimo_list_append", a));
+ return IRValue.none();
+ }
+ if (method == "get") {
+ IRValue idx = this.lowerExpr(args.get(0) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(idx);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ this.emit(IRInstr.call(dst, "__arimo_list_get", a));
+ return dst;
+ }
+ if (method == "set") {
+ IRValue idx = this.lowerExpr(args.get(0) as Expr);
+ IRValue val = this.lowerExpr(args.get(1) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(idx);
+ a.append(val);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.VOID);
+ this.emit(IRInstr.call(dst, "__arimo_list_set", a));
+ return IRValue.none();
+ }
+ if (method == "removeAt") {
+ IRValue idx = this.lowerExpr(args.get(0) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(idx);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.VOID);
+ this.emit(IRInstr.call(dst, "__arimo_list_remove_at", a));
+ return IRValue.none();
+ }
+ if (method == "compareTo") {
+ IRValue other = this.lowerExpr(args.get(0) as Expr);
+ // Simple pointer comparison: 0 if same object, -1 otherwise.
+ String eqL = this.newLabel("lc_eq");
+ String endL = this.newLabel("lc_end");
+ String cmpR = this.newReg();
+ IRValue cmpD = IRValue.reg(cmpR, IRType.I64);
+ this.emit(IRInstr.cmp(objVal, other));
+ this.emit(IRInstr.branch(IROpcode.JE, eqL));
+ this.emit(IRInstr.mov(cmpD, IRValue.ofInt(-1, IRType.I64)));
+ this.emit(IRInstr.jmp(endL));
+ this.emit(IRInstr.label(eqL));
+ this.emit(IRInstr.mov(cmpD, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label(endL));
+ return cmpD;
+ }
+ // Fallback for other list methods
+ String dr5 = this.newReg();
+ IRValue dst5 = IRValue.reg(dr5, IRType.I64);
+ List a5 = List();
+ a5.append(objVal);
+ Integer xi = 0;
+ while (xi < args.length()) {
+ a5.append(this.lowerExpr(args.get(xi) as Expr));
+ xi = xi + 1;
+ }
+ this.emit(IRInstr.call(dst5, "__list_${method}", a5));
+ return dst5;
+ }
+
+ private lowerStringMethod(objVal: IRValue, method: String, args: List) : IRValue {
+ if (method == "length") {
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ List a = List();
+ a.append(objVal);
+ this.emit(IRInstr.call(dst, "__arimo_strlen", a));
+ return dst;
+ }
+ if (method == "compareTo" || method == "equals") {
+ IRValue other = this.lowerExpr(args.get(0) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(other);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ this.emit(IRInstr.call(dst, "__arimo_strcmp", a));
+ return dst;
+ }
+ if (method == "concat") {
+ IRValue other = this.lowerExpr(args.get(0) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(other);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.PTR);
+ this.emit(IRInstr.call(dst, "__arimo_strcat", a));
+ return dst;
+ }
+ if (method == "startsWith") {
+ IRValue prefix = this.lowerExpr(args.get(0) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(prefix);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ this.emit(IRInstr.call(dst, "__arimo_startswith", a));
+ return dst;
+ }
+ if (method == "endsWith") {
+ IRValue suffix = this.lowerExpr(args.get(0) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(suffix);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ this.emit(IRInstr.call(dst, "__arimo_endswith", a));
+ return dst;
+ }
+ if (method == "substring") {
+ IRValue from = this.lowerExpr(args.get(0) as Expr);
+ IRValue to = this.lowerExpr(args.get(1) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(from);
+ a.append(to);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.PTR);
+ this.emit(IRInstr.call(dst, "__arimo_substr", a));
+ return dst;
+ }
+ if (method == "contains") {
+ IRValue sub = this.lowerExpr(args.get(0) as Expr);
+ List a = List(); a.append(objVal); a.append(sub); a.append(IRValue.ofInt(0, IRType.I64));
+ String dr = this.newReg(); this.emit(IRInstr.call(IRValue.reg(dr, IRType.I64), "__arimo_indexof_from", a));
+ String foundR = this.newReg(); IRValue found = IRValue.reg(foundR, IRType.I64);
+ this.emit(IRInstr.mov(found, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.cmp(IRValue.reg(dr, IRType.I64), IRValue.ofInt(-1, IRType.I64)));
+ String ctLbl = this.newLabel("ct_done");
+ this.emit(IRInstr.branch(IROpcode.JE, ctLbl));
+ this.emit(IRInstr.mov(found, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.label(ctLbl));
+ return found;
+ }
+ if (method == "indexOf") {
+ IRValue sub = this.lowerExpr(args.get(0) as Expr);
+ IRValue from = IRValue.ofInt(0, IRType.I64);
+ if (args.length() > 1) { from = this.lowerExpr(args.get(1) as Expr); }
+ List a = List();
+ a.append(objVal);
+ a.append(sub);
+ a.append(from);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ this.emit(IRInstr.call(dst, "__arimo_indexof_from", a));
+ return dst;
+ }
+ if (method == "charCodeAt") {
+ IRValue idx = this.lowerExpr(args.get(0) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(idx);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ this.emit(IRInstr.call(dst, "__arimo_charcodeat", a));
+ return dst;
+ }
+ if (method == "charAt") {
+ IRValue idx = this.lowerExpr(args.get(0) as Expr);
+ List a = List();
+ a.append(objVal);
+ a.append(idx);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.PTR);
+ this.emit(IRInstr.call(dst, "__arimo_charat", a));
+ return dst;
+ }
+ if (method == "isEmpty") {
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.I64);
+ List a = List();
+ a.append(objVal);
+ this.emit(IRInstr.call(dst, "__arimo_strlen", a));
+ String eqL = this.newLabel("isemp_eq");
+ String endL = this.newLabel("isemp_end");
+ String res = this.newReg();
+ IRValue resDst = IRValue.reg(res, IRType.I64);
+ this.emit(IRInstr.cmp(dst, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, eqL));
+ this.emit(IRInstr.mov(resDst, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.jmp(endL));
+ this.emit(IRInstr.label(eqL));
+ this.emit(IRInstr.mov(resDst, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.label(endL));
+ return resDst;
+ }
+ if (method == "toLowerCase" || method == "toLower") {
+ String tlReg = this.newReg();
+ IRValue tlDst = IRValue.reg(tlReg, IRType.PTR);
+ List tlArgs = List(); tlArgs.append(objVal);
+ this.emit(IRInstr.call(tlDst, "__arimo_tolower", tlArgs));
+ return tlDst;
+ }
+ if (method == "toUpperCase" || method == "toUpper") {
+ // Not yet implemented — stub returns original
+ return IRValue.reg(objVal.name, objVal.ty);
+ }
+ if (method == "parseInt" || method == "parseFloat") {
+ // Minimal implementation: iterate chars, accumulate value
+ String lenR = this.newReg();
+ IRValue lenDst = IRValue.reg(lenR, IRType.I64);
+ List la = List(); la.append(objVal);
+ this.emit(IRInstr.call(lenDst, "__arimo_strlen", la));
+ String resultR = this.newReg();
+ IRValue result = IRValue.reg(resultR, IRType.I64);
+ this.emit(IRInstr.mov(result, IRValue.ofInt(0, IRType.I64)));
+ String iR = this.newReg();
+ IRValue iv = IRValue.reg(iR, IRType.I64);
+ this.emit(IRInstr.mov(iv, IRValue.ofInt(0, IRType.I64)));
+ String loopL = this.newLabel("pi_loop");
+ String doneL = this.newLabel("pi_done");
+ this.emit(IRInstr.label(loopL));
+ this.emit(IRInstr.cmp(iv, lenDst));
+ this.emit(IRInstr.branch(IROpcode.JGE, doneL));
+ String cpR = this.newReg();
+ IRValue cp = IRValue.reg(cpR, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, cp, objVal, iv));
+ String chR = this.newReg();
+ IRValue ch = IRValue.reg(chR, IRType.I64);
+ this.emit(IRInstr.load(ch, cp, IRType.I8));
+ // If ch < '0' or ch > '9', break
+ String skipL = this.newLabel("pi_skip");
+ this.emit(IRInstr.cmp(ch, IRValue.ofInt(48, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JL, skipL));
+ this.emit(IRInstr.cmp(ch, IRValue.ofInt(57, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JG, skipL));
+ // result = result * 10 + (ch - '0')
+ String tmpR = this.newReg();
+ IRValue tmp = IRValue.reg(tmpR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, tmp, result, IRValue.ofInt(10, IRType.I64)));
+ String tmp2R = this.newReg();
+ IRValue tmp2 = IRValue.reg(tmp2R, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, tmp2, ch, IRValue.ofInt(48, IRType.I64)));
+ this.emit(IRInstr.binop(IROpcode.ADD, result, tmp, tmp2));
+ this.emit(IRInstr.label(skipL));
+ this.emit(IRInstr.binop(IROpcode.ADD, iv, iv, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp(loopL));
+ this.emit(IRInstr.label(doneL));
+ return result;
+ }
+ // Fallback
+ List fa = List();
+ fa.append(objVal);
+ Integer xi = 0;
+ while (xi < args.length()) {
+ fa.append(this.lowerExpr(args.get(xi) as Expr));
+ xi = xi + 1;
+ }
+ String fdr = this.newReg();
+ IRValue fdst = IRValue.reg(fdr, IRType.I64);
+ this.emit(IRInstr.call(fdst, "__str_${method}", fa));
+ return fdst;
+ }
+
+ // ===== Method / constructor body lowering =====
+
+ private lowerMethod(className: String, md: MethodDecl) {
+ String fnName = "${className}__${md.name}";
+ this.beginFn(fnName, IRType.I64);
+ this.resetFnContext();
+ this.curClass = className;
+
+ if (!md.isStatic) {
+ this.addParamToLast("__this", IRType.PTR);
+ this.thisReg = "__this";
+ }
+ Integer pi = 0;
+ while (pi < md.params.length()) {
+ Param p = md.params.get(pi) as Param;
+ this.addParamToLast(p.name, IRType.I64);
+ pi = pi + 1;
+ }
+
+ this.emit(IRInstr.label("entry"));
+
+ if (!md.isStatic) {
+ this.varDefine("__this", "__this");
+ this.varSetClass("__this", className);
+ }
+ Integer pi2 = 0;
+ while (pi2 < md.params.length()) {
+ Param p2 = md.params.get(pi2) as Param;
+ this.varDefine(p2.name, p2.name);
+ String pCls = this.tyToClass(p2.ty);
+ if (pCls != "") { this.varSetClass(p2.name, pCls); }
+ pi2 = pi2 + 1;
+ }
+
+ this.lowerBody(md.body);
+ IRFunction curFn = this.lastFn();
+ Boolean needsRet = true;
+ if (curFn.instrs.length() > 0) {
+ IRInstr lastInstr = curFn.instrs.get(curFn.instrs.length() - 1) as IRInstr;
+ if (lastInstr.op == IROpcode.RET) { needsRet = false; }
+ }
+ if (needsRet) {
+ this.emit(IRInstr.ret(IRValue.ofInt(0, IRType.I64)));
+ }
+
+ if (md.isStatic && md.name == "main" && this.mainFn == "") {
+ this.mainFn = fnName;
+ }
+ }
+
+ private lowerConstructor(className: String, ctor: ConstructorDecl) {
+ String fnName = "${className}__init";
+ this.beginFn(fnName, IRType.VOID);
+ this.resetFnContext();
+ this.curClass = className;
+
+ this.addParamToLast("__this", IRType.PTR);
+ this.thisReg = "__this";
+ Integer pi = 0;
+ while (pi < ctor.params.length()) {
+ Param p = ctor.params.get(pi) as Param;
+ this.addParamToLast(p.name, IRType.I64);
+ pi = pi + 1;
+ }
+
+ this.emit(IRInstr.label("entry"));
+ this.varDefine("__this", "__this");
+ this.varSetClass("__this", className);
+ Integer pi2 = 0;
+ while (pi2 < ctor.params.length()) {
+ Param p2 = ctor.params.get(pi2) as Param;
+ this.varDefine(p2.name, p2.name);
+ String pCls = this.tyToClass(p2.ty);
+ if (pCls != "") { this.varSetClass(p2.name, pCls); }
+ pi2 = pi2 + 1;
+ }
+
+ this.lowerBody(ctor.body);
+ this.emit(IRInstr.retVoid());
+ }
+
+ private lowerClass(cd: ClassDecl) {
+ if (cd.ctor != null) {
+ ConstructorDecl ctor = cd.ctor as ConstructorDecl;
+ if (ctor.body != null) {
+ this.lowerConstructor(cd.name, ctor);
+ }
+ }
+ Integer i = 0;
+ while (i < cd.methods.length()) {
+ MethodDecl md = cd.methods.get(i) as MethodDecl;
+ if (md.body != null) {
+ this.lowerMethod(cd.name, md);
+ }
+ i = i + 1;
+ }
+ }
+
+ private lowerModule(m: ArimoModule) {
+ Integer i = 0;
+ while (i < m.items.length()) {
+ Item it = m.items.get(i) as Item;
+ if (it.kind == ItemKind.CLASS) {
+ this.lowerClass(it.classDecl);
+ }
+ i = i + 1;
+ }
+ }
+
+ // ===== Print-int helpers (avoid two-call register pressure in caller) =====
+
+ private generatePrintlnInt(name: String, addNewline: Boolean) {
+ this.beginFn(name, IRType.VOID);
+ this.addParamToLast("n", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+
+ IRValue nv = IRValue.reg("n", IRType.I64);
+
+ // Convert to string
+ String tsReg = this.newReg();
+ IRValue tsDst = IRValue.reg(tsReg, IRType.PTR);
+ List tsArgs = List();
+ tsArgs.append(nv);
+ this.emit(IRInstr.call(tsDst, "__arimo_i64_to_str", tsArgs));
+
+ // Print
+ String target = "__arimo_println";
+ if (!addNewline) { target = "__arimo_print"; }
+ List callArgs = List();
+ callArgs.append(tsDst);
+ String dr = this.newReg();
+ IRValue dst = IRValue.reg(dr, IRType.VOID);
+ this.emit(IRInstr.call(dst, target, callArgs));
+ this.emit(IRInstr.retVoid());
+ }
+
+ // ===== Print helpers =====
+
+ private generatePrintHelper(name: String, addNewline: Boolean) {
+ this.beginFn(name, IRType.VOID);
+ this.addParamToLast("str", IRType.PTR);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+
+ IRValue strSaved = IRValue.reg("str_saved", IRType.PTR);
+ this.emit(IRInstr.mov(strSaved, IRValue.reg("str", IRType.PTR)));
+
+ String nlName = this.internStr("\n");
+ IRValue strPtr = IRValue.reg("str_saved", IRType.PTR);
+
+ String lenReg = "plen";
+ IRValue lenDst = IRValue.reg(lenReg, IRType.I64);
+ List lenArgs = List();
+ lenArgs.append(strPtr);
+ this.emit(IRInstr.call(lenDst, "__arimo_strlen", lenArgs));
+
+ if (this.linux) {
+ // write(1, str, len)
+ List wrArgs = List();
+ wrArgs.append(IRValue.ofInt(1, IRType.I64));
+ wrArgs.append(strPtr);
+ wrArgs.append(IRValue.reg("plen", IRType.I64));
+ IRValue wrDst = IRValue.reg("pwr", IRType.VOID);
+ this.emit(IRInstr.syscall(wrDst, 1, wrArgs));
+ if (addNewline) {
+ List wrArgs2 = List();
+ wrArgs2.append(IRValue.ofInt(1, IRType.I64));
+ wrArgs2.append(IRValue.global(nlName));
+ wrArgs2.append(IRValue.ofInt(1, IRType.I64));
+ IRValue wrDst2 = IRValue.reg("pwr2", IRType.VOID);
+ this.emit(IRInstr.syscall(wrDst2, 1, wrArgs2));
+ }
+ } else {
+ IRValue hDst = IRValue.reg("ph_h", IRType.I64);
+ List hsArgs = List();
+ hsArgs.append(IRValue.ofInt(-11, IRType.I64));
+ this.emit(IRInstr.call(hDst, "__ext__GetStdHandle", hsArgs));
+
+ IRValue bwPtr = IRValue.reg("bwPtr", IRType.PTR);
+ this.emit(IRInstr.alloc(bwPtr, 4));
+
+ List wfArgs = List();
+ wfArgs.append(IRValue.reg("ph_h", IRType.I64));
+ wfArgs.append(strPtr);
+ wfArgs.append(IRValue.reg("plen", IRType.I64));
+ wfArgs.append(IRValue.reg("bwPtr", IRType.PTR));
+ wfArgs.append(IRValue.ofInt(0, IRType.I64));
+ IRValue wfDst = IRValue.reg("pwf", IRType.VOID);
+ this.emit(IRInstr.call(wfDst, "__ext__WriteFile", wfArgs));
+
+ if (addNewline) {
+ List wfArgs2 = List();
+ wfArgs2.append(IRValue.reg("ph_h", IRType.I64));
+ wfArgs2.append(IRValue.global(nlName));
+ wfArgs2.append(IRValue.ofInt(1, IRType.I64));
+ wfArgs2.append(IRValue.reg("bwPtr", IRType.PTR));
+ wfArgs2.append(IRValue.ofInt(0, IRType.I64));
+ IRValue wfDst2 = IRValue.reg("pwf2", IRType.VOID);
+ this.emit(IRInstr.call(wfDst2, "__ext__WriteFile", wfArgs2));
+ }
+ }
+ this.emit(IRInstr.retVoid());
+ }
+
+ // ===== Strlen helper =====
+
+ private generateStrlenHelper() {
+ this.beginFn("__arimo_strlen", IRType.I64);
+ this.addParamToLast("s", IRType.PTR);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue sBase = IRValue.reg("sBase", IRType.PTR);
+ this.emit(IRInstr.mov(sBase, IRValue.reg("s", IRType.PTR)));
+ IRValue cnt = IRValue.reg("cnt", IRType.I64);
+ this.emit(IRInstr.mov(cnt, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("strlen_loop"));
+ IRValue idx = IRValue.reg("cidx", IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, idx, IRValue.reg("sBase", IRType.PTR), IRValue.reg("cnt", IRType.I64)));
+ IRValue ch = IRValue.reg("ch", IRType.I64);
+ this.emit(IRInstr.load(ch, IRValue.reg("cidx", IRType.I64), IRType.I8));
+ this.emit(IRInstr.cmp(IRValue.reg("ch", IRType.I64), IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, "strlen_done"));
+ IRValue cnt2 = IRValue.reg("cnt", IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, cnt2, IRValue.reg("cnt", IRType.I64), IRValue.ofInt(1, IRType.I64)));
+ IRValue sKeep = IRValue.reg("sKeep", IRType.PTR);
+ this.emit(IRInstr.mov(sKeep, IRValue.reg("sBase", IRType.PTR)));
+ this.emit(IRInstr.jmp("strlen_loop"));
+ this.emit(IRInstr.label("strlen_done"));
+ this.emit(IRInstr.ret(IRValue.reg("cnt", IRType.I64)));
+ }
+
+ // ===== List runtime =====
+
+ private generateListNew() {
+ this.beginFn("__arimo_list_new", IRType.PTR);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue listPtr = this.emitHeapAlloc(IRValue.ofInt(24, IRType.I64));
+ IRValue dataPtr = this.emitHeapAlloc(IRValue.ofInt(64, IRType.I64));
+ this.emitFieldStoreS(this.emitFieldPtr(listPtr, 0), dataPtr);
+ this.emitFieldStoreS(this.emitFieldPtr(listPtr, 8), IRValue.ofInt(0, IRType.I64));
+ this.emitFieldStoreS(this.emitFieldPtr(listPtr, 16), IRValue.ofInt(8, IRType.I64));
+ this.emit(IRInstr.ret(listPtr));
+ }
+
+ private generateListLength() {
+ this.beginFn("__arimo_list_length", IRType.I64);
+ this.addParamToLast("list", IRType.PTR);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue listPar = IRValue.reg("list", IRType.PTR);
+ IRValue lenVal = this.emitFieldLoad(listPar, 8);
+ this.emit(IRInstr.ret(lenVal));
+ }
+
+ private generateListGet() {
+ this.beginFn("__arimo_list_get", IRType.I64);
+ this.addParamToLast("list", IRType.PTR);
+ this.addParamToLast("idx", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue listPar = IRValue.reg("list", IRType.PTR);
+ IRValue idxPar = IRValue.reg("idx", IRType.I64);
+ IRValue data = this.emitFieldLoad(listPar, 0);
+ String offReg = this.newReg();
+ IRValue off = IRValue.reg(offReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, off, idxPar, IRValue.ofInt(8, IRType.I64)));
+ String pReg = this.newReg();
+ IRValue slot = IRValue.reg(pReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, slot, data, off));
+ String elemReg = this.newReg();
+ IRValue elem = IRValue.reg(elemReg, IRType.I64);
+ this.emit(IRInstr.load(elem, slot, IRType.I64));
+ this.emit(IRInstr.ret(elem));
+ }
+
+ private generateListSet() {
+ this.beginFn("__arimo_list_set", IRType.VOID);
+ this.addParamToLast("list", IRType.PTR);
+ this.addParamToLast("idx", IRType.I64);
+ this.addParamToLast("val", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue listPar = IRValue.reg("list", IRType.PTR);
+ IRValue idxPar = IRValue.reg("idx", IRType.I64);
+ IRValue valPar = IRValue.reg("val", IRType.I64);
+ IRValue data = this.emitFieldLoad(listPar, 0);
+ String offReg = this.newReg();
+ IRValue off = IRValue.reg(offReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, off, idxPar, IRValue.ofInt(8, IRType.I64)));
+ String pReg = this.newReg();
+ IRValue slot = IRValue.reg(pReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, slot, data, off));
+ this.emit(IRInstr.store(valPar, slot, IRType.I64));
+ this.emit(IRInstr.retVoid());
+ }
+
+ private generateListAppend() {
+ this.beginFn("__arimo_list_append", IRType.VOID);
+ this.addParamToLast("list", IRType.PTR);
+ this.addParamToLast("val", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue listPar = IRValue.reg("list", IRType.PTR);
+ IRValue valPar = IRValue.reg("val", IRType.I64);
+
+ IRValue lenVal = this.emitFieldLoad(listPar, 8);
+ IRValue capVal = this.emitFieldLoad(listPar, 16);
+
+ String noResL = this.newLabel("no_resize");
+ this.emit(IRInstr.cmp(lenVal, capVal));
+ this.emit(IRInstr.branch(IROpcode.JL, noResL));
+
+ // resize: new_cap = cap*2, alloc new data, copy, update list
+ String ncReg = this.newReg();
+ IRValue newCap = IRValue.reg(ncReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, newCap, capVal, capVal));
+
+ String nszReg = this.newReg();
+ IRValue newSz = IRValue.reg(nszReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, newSz, newCap, IRValue.ofInt(8, IRType.I64)));
+
+ IRValue newData = this.emitHeapAlloc(newSz);
+
+ IRValue oldData = this.emitFieldLoad(listPar, 0);
+
+ String ciReg = this.newReg();
+ IRValue copyI = IRValue.reg(ciReg, IRType.I64);
+ this.emit(IRInstr.mov(copyI, IRValue.ofInt(0, IRType.I64)));
+
+ String cpLoopL = this.newLabel("cp_loop");
+ String cpDoneL = this.newLabel("cp_done");
+ this.emit(IRInstr.label(cpLoopL));
+ this.emit(IRInstr.cmp(copyI, lenVal));
+ this.emit(IRInstr.branch(IROpcode.JGE, cpDoneL));
+
+ String coReg = this.newReg();
+ IRValue copyOff = IRValue.reg(coReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, copyOff, copyI, IRValue.ofInt(8, IRType.I64)));
+
+ String spReg = this.newReg();
+ IRValue srcPtr = IRValue.reg(spReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, srcPtr, oldData, copyOff));
+
+ String elReg = this.newReg();
+ IRValue elem = IRValue.reg(elReg, IRType.I64);
+ this.emit(IRInstr.load(elem, srcPtr, IRType.I64));
+
+ String dpReg = this.newReg();
+ IRValue dstPtr = IRValue.reg(dpReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, dstPtr, newData, copyOff));
+ this.emit(IRInstr.store(elem, dstPtr, IRType.I64));
+
+ this.emit(IRInstr.binop(IROpcode.ADD, copyI, copyI, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp(cpLoopL));
+ this.emit(IRInstr.label(cpDoneL));
+
+ IRValue ndPtr = this.emitFieldPtr(listPar, 0);
+ this.emit(IRInstr.store(newData, ndPtr, IRType.I64));
+ IRValue ncPtr = this.emitFieldPtr(listPar, 16);
+ this.emit(IRInstr.store(newCap, ncPtr, IRType.I64));
+
+ this.emit(IRInstr.label(noResL));
+
+ IRValue data2 = this.emitFieldLoad(listPar, 0);
+ String loReg = this.newReg();
+ IRValue lenOff = IRValue.reg(loReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, lenOff, lenVal, IRValue.ofInt(8, IRType.I64)));
+ String slReg = this.newReg();
+ IRValue slot = IRValue.reg(slReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, slot, data2, lenOff));
+ this.emit(IRInstr.store(valPar, slot, IRType.I64));
+
+ String nlReg = this.newReg();
+ IRValue newLen = IRValue.reg(nlReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, newLen, lenVal, IRValue.ofInt(1, IRType.I64)));
+ IRValue lfPtr = this.emitFieldPtr(listPar, 8);
+ this.emit(IRInstr.store(newLen, lfPtr, IRType.I64));
+ this.emit(IRInstr.retVoid());
+ }
+
+ private generateListRemoveAt() {
+ this.beginFn("__arimo_list_remove_at", IRType.VOID);
+ this.addParamToLast("list", IRType.PTR);
+ this.addParamToLast("idx", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue listPar = IRValue.reg("list", IRType.PTR);
+ IRValue idxPar = IRValue.reg("idx", IRType.I64);
+
+ IRValue lenVal = this.emitFieldLoad(listPar, 8);
+ IRValue data = this.emitFieldLoad(listPar, 0);
+
+ String iReg = this.newReg();
+ IRValue shiftI = IRValue.reg(iReg, IRType.I64);
+ this.emit(IRInstr.mov(shiftI, idxPar));
+
+ String shLoopL = this.newLabel("sh_loop");
+ String shDoneL = this.newLabel("sh_done");
+
+ String limReg = this.newReg();
+ IRValue lim = IRValue.reg(limReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, lim, lenVal, IRValue.ofInt(1, IRType.I64)));
+
+ this.emit(IRInstr.label(shLoopL));
+ this.emit(IRInstr.cmp(shiftI, lim));
+ this.emit(IRInstr.branch(IROpcode.JGE, shDoneL));
+
+ String no1Reg = this.newReg();
+ IRValue nextOff = IRValue.reg(no1Reg, IRType.I64);
+ String ni1Reg = this.newReg();
+ IRValue nextIdx = IRValue.reg(ni1Reg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, nextIdx, shiftI, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.binop(IROpcode.MUL, nextOff, nextIdx, IRValue.ofInt(8, IRType.I64)));
+
+ String np1Reg = this.newReg();
+ IRValue nextPtr = IRValue.reg(np1Reg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, nextPtr, data, nextOff));
+
+ String ev1Reg = this.newReg();
+ IRValue elemVal = IRValue.reg(ev1Reg, IRType.I64);
+ this.emit(IRInstr.load(elemVal, nextPtr, IRType.I64));
+
+ String co1Reg = this.newReg();
+ IRValue currOff = IRValue.reg(co1Reg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MUL, currOff, shiftI, IRValue.ofInt(8, IRType.I64)));
+ String cp1Reg = this.newReg();
+ IRValue currPtr = IRValue.reg(cp1Reg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, currPtr, data, currOff));
+ this.emit(IRInstr.store(elemVal, currPtr, IRType.I64));
+
+ this.emit(IRInstr.binop(IROpcode.ADD, shiftI, shiftI, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp(shLoopL));
+ this.emit(IRInstr.label(shDoneL));
+
+ String nl2Reg = this.newReg();
+ IRValue newLen = IRValue.reg(nl2Reg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, newLen, lenVal, IRValue.ofInt(1, IRType.I64)));
+ IRValue lfPtr = this.emitFieldPtr(listPar, 8);
+ this.emit(IRInstr.store(newLen, lfPtr, IRType.I64));
+ this.emit(IRInstr.retVoid());
+ }
+
+ // ===== String builtins =====
+
+ private generateStrCmp() {
+ this.beginFn("__arimo_strcmp", IRType.I64);
+ this.addParamToLast("a", IRType.PTR);
+ this.addParamToLast("b", IRType.PTR);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue aP = IRValue.reg("a", IRType.PTR);
+ IRValue bP = IRValue.reg("b", IRType.PTR);
+ IRValue i = IRValue.reg("sci", IRType.I64);
+ this.emit(IRInstr.mov(i, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("scmp_loop"));
+
+ String pca = this.newReg();
+ IRValue pa = IRValue.reg(pca, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, pa, aP, i));
+ String cca = this.newReg();
+ IRValue ca = IRValue.reg(cca, IRType.I64);
+ this.emit(IRInstr.load(ca, pa, IRType.I8));
+
+ String pcb = this.newReg();
+ IRValue pb = IRValue.reg(pcb, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, pb, bP, i));
+ String ccb = this.newReg();
+ IRValue cb = IRValue.reg(ccb, IRType.I64);
+ this.emit(IRInstr.load(cb, pb, IRType.I8));
+
+ this.emit(IRInstr.cmp(ca, cb));
+ this.emit(IRInstr.branch(IROpcode.JNE, "scmp_ne"));
+
+ this.emit(IRInstr.cmp(ca, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, "scmp_eq"));
+
+ this.emit(IRInstr.binop(IROpcode.ADD, i, i, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("scmp_loop"));
+
+ this.emit(IRInstr.label("scmp_ne"));
+ String dr1 = this.newReg();
+ IRValue diff = IRValue.reg(dr1, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, diff, ca, cb));
+ this.emit(IRInstr.ret(diff));
+
+ this.emit(IRInstr.label("scmp_eq"));
+ this.emit(IRInstr.ret(IRValue.ofInt(0, IRType.I64)));
+ }
+
+ private generateStrCat() {
+ this.beginFn("__arimo_strcat", IRType.PTR);
+ this.addParamToLast("a", IRType.PTR);
+ this.addParamToLast("b", IRType.PTR);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue aP = IRValue.reg("a", IRType.PTR);
+ IRValue bP = IRValue.reg("b", IRType.PTR);
+
+ // alen = strlen(a)
+ String alenReg = this.newReg();
+ IRValue alen = IRValue.reg(alenReg, IRType.I64);
+ List sla = List();
+ sla.append(aP);
+ this.emit(IRInstr.call(alen, "__arimo_strlen", sla));
+
+ // blen = strlen(b)
+ String blenReg = this.newReg();
+ IRValue blen = IRValue.reg(blenReg, IRType.I64);
+ List slb = List();
+ slb.append(bP);
+ this.emit(IRInstr.call(blen, "__arimo_strlen", slb));
+
+ // total = alen + blen + 1
+ String tlReg = this.newReg();
+ IRValue total = IRValue.reg(tlReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, total, alen, blen));
+ String tl2Reg = this.newReg();
+ IRValue total2 = IRValue.reg(tl2Reg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, total2, total, IRValue.ofInt(1, IRType.I64)));
+
+ IRValue buf = this.emitHeapAlloc(total2);
+
+ // copy a
+ String aiReg = this.newReg();
+ IRValue ai = IRValue.reg(aiReg, IRType.I64);
+ this.emit(IRInstr.mov(ai, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("scat_a"));
+ this.emit(IRInstr.cmp(ai, alen));
+ this.emit(IRInstr.branch(IROpcode.JGE, "scat_a_done"));
+ String apReg = this.newReg();
+ IRValue apv = IRValue.reg(apReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, apv, aP, ai));
+ String acReg = this.newReg();
+ IRValue ac = IRValue.reg(acReg, IRType.I64);
+ this.emit(IRInstr.load(ac, apv, IRType.I8));
+ String dpReg = this.newReg();
+ IRValue dpv = IRValue.reg(dpReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, dpv, buf, ai));
+ this.emit(IRInstr.store(ac, dpv, IRType.I8));
+ this.emit(IRInstr.binop(IROpcode.ADD, ai, ai, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("scat_a"));
+ this.emit(IRInstr.label("scat_a_done"));
+
+ // copy b
+ String biReg = this.newReg();
+ IRValue bi = IRValue.reg(biReg, IRType.I64);
+ this.emit(IRInstr.mov(bi, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("scat_b"));
+ this.emit(IRInstr.cmp(bi, blen));
+ this.emit(IRInstr.branch(IROpcode.JGE, "scat_b_done"));
+ String bpReg = this.newReg();
+ IRValue bpv = IRValue.reg(bpReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, bpv, bP, bi));
+ String bcReg = this.newReg();
+ IRValue bc = IRValue.reg(bcReg, IRType.I64);
+ this.emit(IRInstr.load(bc, bpv, IRType.I8));
+ String dbReg = this.newReg();
+ IRValue dbv = IRValue.reg(dbReg, IRType.PTR);
+ String dbsReg = this.newReg();
+ IRValue dbs = IRValue.reg(dbsReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, dbs, alen, bi));
+ this.emit(IRInstr.binop(IROpcode.ADD, dbv, buf, dbs));
+ this.emit(IRInstr.store(bc, dbv, IRType.I8));
+ this.emit(IRInstr.binop(IROpcode.ADD, bi, bi, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("scat_b"));
+ this.emit(IRInstr.label("scat_b_done"));
+
+ // null terminate
+ String nullPReg = this.newReg();
+ IRValue nullP = IRValue.reg(nullPReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, nullP, buf, total));
+ this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), nullP, IRType.I8));
+
+ this.emit(IRInstr.ret(buf));
+ }
+
+ private generateSubstr() {
+ this.beginFn("__arimo_substr", IRType.PTR);
+ this.addParamToLast("s", IRType.PTR);
+ this.addParamToLast("from", IRType.I64);
+ this.addParamToLast("to", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue sP = IRValue.reg("s", IRType.PTR);
+ IRValue fromP = IRValue.reg("from", IRType.I64);
+ IRValue toP = IRValue.reg("to", IRType.I64);
+
+ String lenReg = this.newReg();
+ IRValue len = IRValue.reg(lenReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, len, toP, fromP));
+
+ String aszReg = this.newReg();
+ IRValue asz = IRValue.reg(aszReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, asz, len, IRValue.ofInt(1, IRType.I64)));
+
+ IRValue buf = this.emitHeapAlloc(asz);
+
+ String iReg = this.newReg();
+ IRValue si = IRValue.reg(iReg, IRType.I64);
+ this.emit(IRInstr.mov(si, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("sub_loop"));
+ this.emit(IRInstr.cmp(si, len));
+ this.emit(IRInstr.branch(IROpcode.JGE, "sub_done"));
+
+ String srcIdxReg = this.newReg();
+ IRValue srcIdx = IRValue.reg(srcIdxReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, srcIdx, fromP, si));
+ String spReg = this.newReg();
+ IRValue sp = IRValue.reg(spReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, sp, sP, srcIdx));
+ String chReg = this.newReg();
+ IRValue ch = IRValue.reg(chReg, IRType.I64);
+ this.emit(IRInstr.load(ch, sp, IRType.I8));
+ String dpReg = this.newReg();
+ IRValue dp = IRValue.reg(dpReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, dp, buf, si));
+ this.emit(IRInstr.store(ch, dp, IRType.I8));
+ this.emit(IRInstr.binop(IROpcode.ADD, si, si, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("sub_loop"));
+ this.emit(IRInstr.label("sub_done"));
+
+ String nPReg = this.newReg();
+ IRValue nP = IRValue.reg(nPReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, nP, buf, len));
+ this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), nP, IRType.I8));
+ this.emit(IRInstr.ret(buf));
+ }
+
+ private generateStartsWith() {
+ this.beginFn("__arimo_startswith", IRType.I64);
+ this.addParamToLast("s", IRType.PTR);
+ this.addParamToLast("prefix", IRType.PTR);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue sP = IRValue.reg("s", IRType.PTR);
+ IRValue pfP = IRValue.reg("prefix", IRType.PTR);
+ IRValue si = IRValue.reg("swi", IRType.I64);
+ this.emit(IRInstr.mov(si, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("sw_loop"));
+
+ String ppp = this.newReg();
+ IRValue pp = IRValue.reg(ppp, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, pp, pfP, si));
+ String pcc = this.newReg();
+ IRValue pc = IRValue.reg(pcc, IRType.I64);
+ this.emit(IRInstr.load(pc, pp, IRType.I8));
+ this.emit(IRInstr.cmp(pc, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, "sw_yes"));
+
+ String spp = this.newReg();
+ IRValue sp = IRValue.reg(spp, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, sp, sP, si));
+ String scc = this.newReg();
+ IRValue sc = IRValue.reg(scc, IRType.I64);
+ this.emit(IRInstr.load(sc, sp, IRType.I8));
+ this.emit(IRInstr.cmp(sc, pc));
+ this.emit(IRInstr.branch(IROpcode.JNE, "sw_no"));
+
+ this.emit(IRInstr.binop(IROpcode.ADD, si, si, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("sw_loop"));
+ this.emit(IRInstr.label("sw_yes"));
+ this.emit(IRInstr.ret(IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.label("sw_no"));
+ this.emit(IRInstr.ret(IRValue.ofInt(0, IRType.I64)));
+ }
+
+ private generateEndsWith() {
+ this.beginFn("__arimo_endswith", IRType.I64);
+ this.addParamToLast("s", IRType.PTR);
+ this.addParamToLast("suffix", IRType.PTR);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue sP = IRValue.reg("s", IRType.PTR);
+ IRValue sfP = IRValue.reg("suffix", IRType.PTR);
+
+ String slenR = this.newReg();
+ IRValue slen = IRValue.reg(slenR, IRType.I64);
+ List sla = List();
+ sla.append(sP);
+ this.emit(IRInstr.call(slen, "__arimo_strlen", sla));
+
+ String sflenR = this.newReg();
+ IRValue sflen = IRValue.reg(sflenR, IRType.I64);
+ List slb = List();
+ slb.append(sfP);
+ this.emit(IRInstr.call(sflen, "__arimo_strlen", slb));
+
+ this.emit(IRInstr.cmp(sflen, slen));
+ this.emit(IRInstr.branch(IROpcode.JG, "ew_no"));
+
+ String offR = this.newReg();
+ IRValue off = IRValue.reg(offR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, off, slen, sflen));
+
+ IRValue ewi = IRValue.reg("ewi", IRType.I64);
+ this.emit(IRInstr.mov(ewi, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("ew_loop"));
+
+ String sfpp = this.newReg();
+ IRValue sfp = IRValue.reg(sfpp, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, sfp, sfP, ewi));
+ String sfcc = this.newReg();
+ IRValue sfc = IRValue.reg(sfcc, IRType.I64);
+ this.emit(IRInstr.load(sfc, sfp, IRType.I8));
+ this.emit(IRInstr.cmp(sfc, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, "ew_yes"));
+
+ String sidxR = this.newReg();
+ IRValue sidx = IRValue.reg(sidxR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, sidx, off, ewi));
+ String spp = this.newReg();
+ IRValue sp = IRValue.reg(spp, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, sp, sP, sidx));
+ String scc = this.newReg();
+ IRValue sc = IRValue.reg(scc, IRType.I64);
+ this.emit(IRInstr.load(sc, sp, IRType.I8));
+ this.emit(IRInstr.cmp(sc, sfc));
+ this.emit(IRInstr.branch(IROpcode.JNE, "ew_no"));
+
+ this.emit(IRInstr.binop(IROpcode.ADD, ewi, ewi, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("ew_loop"));
+ this.emit(IRInstr.label("ew_yes"));
+ this.emit(IRInstr.ret(IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.label("ew_no"));
+ this.emit(IRInstr.ret(IRValue.ofInt(0, IRType.I64)));
+ }
+
+ private generateIndexOf() {
+ this.beginFn("__arimo_indexof_from", IRType.I64);
+ this.addParamToLast("s", IRType.PTR);
+ this.addParamToLast("sub", IRType.PTR);
+ this.addParamToLast("from", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue sP = IRValue.reg("s", IRType.PTR);
+ IRValue subP = IRValue.reg("sub", IRType.PTR);
+ IRValue fromV = IRValue.reg("from", IRType.I64);
+
+ String slenR = this.newReg();
+ IRValue slen = IRValue.reg(slenR, IRType.I64);
+ List sla = List();
+ sla.append(sP);
+ this.emit(IRInstr.call(slen, "__arimo_strlen", sla));
+
+ String sublenR = this.newReg();
+ IRValue sublen = IRValue.reg(sublenR, IRType.I64);
+ List slb = List();
+ slb.append(subP);
+ this.emit(IRInstr.call(sublen, "__arimo_strlen", slb));
+
+ IRValue oi = IRValue.reg("io_i", IRType.I64);
+ this.emit(IRInstr.mov(oi, fromV));
+ this.emit(IRInstr.label("io_outer"));
+
+ String limR = this.newReg();
+ IRValue lim = IRValue.reg(limR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, lim, slen, sublen));
+ this.emit(IRInstr.cmp(oi, lim));
+ this.emit(IRInstr.branch(IROpcode.JG, "io_notfound"));
+
+ IRValue ij = IRValue.reg("io_j", IRType.I64);
+ this.emit(IRInstr.mov(ij, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("io_inner"));
+
+ String spjR = this.newReg();
+ IRValue spj = IRValue.reg(spjR, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, spj, subP, ij));
+ String scjR = this.newReg();
+ IRValue scj = IRValue.reg(scjR, IRType.I64);
+ this.emit(IRInstr.load(scj, spj, IRType.I8));
+ this.emit(IRInstr.cmp(scj, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, "io_match"));
+
+ String soffR = this.newReg();
+ IRValue soff = IRValue.reg(soffR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, soff, oi, ij));
+ String sppR = this.newReg();
+ IRValue spp = IRValue.reg(sppR, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, spp, sP, soff));
+ String sccR = this.newReg();
+ IRValue scc = IRValue.reg(sccR, IRType.I64);
+ this.emit(IRInstr.load(scc, spp, IRType.I8));
+ this.emit(IRInstr.cmp(scc, scj));
+ this.emit(IRInstr.branch(IROpcode.JNE, "io_nomatch"));
+
+ this.emit(IRInstr.binop(IROpcode.ADD, ij, ij, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("io_inner"));
+
+ this.emit(IRInstr.label("io_match"));
+ this.emit(IRInstr.ret(oi));
+
+ this.emit(IRInstr.label("io_nomatch"));
+ this.emit(IRInstr.binop(IROpcode.ADD, oi, oi, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("io_outer"));
+
+ this.emit(IRInstr.label("io_notfound"));
+ this.emit(IRInstr.ret(IRValue.ofInt(-1, IRType.I64)));
+ }
+
+ private generateCharCodeAt() {
+ this.beginFn("__arimo_charcodeat", IRType.I64);
+ this.addParamToLast("s", IRType.PTR);
+ this.addParamToLast("idx", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue sP = IRValue.reg("s", IRType.PTR);
+ IRValue idx = IRValue.reg("idx", IRType.I64);
+ String pReg = this.newReg();
+ IRValue p = IRValue.reg(pReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, p, sP, idx));
+ String cReg = this.newReg();
+ IRValue c = IRValue.reg(cReg, IRType.I64);
+ this.emit(IRInstr.load(c, p, IRType.I8));
+ this.emit(IRInstr.ret(c));
+ }
+
+ private generateCharAt() {
+ this.beginFn("__arimo_charat", IRType.PTR);
+ this.addParamToLast("s", IRType.PTR);
+ this.addParamToLast("idx", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue sP = IRValue.reg("s", IRType.PTR);
+ IRValue idx = IRValue.reg("idx", IRType.I64);
+ String pReg = this.newReg();
+ IRValue p = IRValue.reg(pReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, p, sP, idx));
+ String cReg = this.newReg();
+ IRValue c = IRValue.reg(cReg, IRType.I64);
+ this.emit(IRInstr.load(c, p, IRType.I8));
+ IRValue buf = this.emitHeapAlloc(IRValue.ofInt(2, IRType.I64));
+ this.emit(IRInstr.store(c, buf, IRType.I8));
+ String npReg = this.newReg();
+ IRValue np = IRValue.reg(npReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, np, buf, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), np, IRType.I8));
+ this.emit(IRInstr.ret(buf));
+ }
+
+ private generateI64ToStr() {
+ this.beginFn("__arimo_i64_to_str", IRType.PTR);
+ this.addParamToLast("n", IRType.I64);
+ this.resetFnContext();
+ this.emit(IRInstr.label("entry"));
+ IRValue nv = IRValue.reg("n", IRType.I64);
+
+ // Special case: n == 0
+ this.emit(IRInstr.cmp(nv, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JNE, "its_nonzero"));
+ IRValue zeroBuf = this.emitHeapAlloc(IRValue.ofInt(4, IRType.I64));
+ String zp0R = this.newReg();
+ IRValue zp0 = IRValue.reg(zp0R, IRType.PTR);
+ this.emit(IRInstr.mov(zp0, zeroBuf));
+ this.emit(IRInstr.store(IRValue.ofInt(48, IRType.I64), zp0, IRType.I8)); // '0'=48
+ String zp1R = this.newReg();
+ IRValue zp1 = IRValue.reg(zp1R, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, zp1, zeroBuf, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), zp1, IRType.I8));
+ this.emit(IRInstr.ret(zeroBuf));
+
+ this.emit(IRInstr.label("its_nonzero"));
+ // neg = 0; abs_n = n; if n < 0: neg=1, abs_n = 0-n
+ IRValue neg = IRValue.reg("its_neg", IRType.I64);
+ IRValue abs_n = IRValue.reg("its_absn", IRType.I64);
+ this.emit(IRInstr.mov(neg, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.mov(abs_n, nv));
+ this.emit(IRInstr.cmp(nv, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JGE, "its_pos"));
+ this.emit(IRInstr.mov(neg, IRValue.ofInt(1, IRType.I64)));
+ String anR = this.newReg();
+ IRValue an = IRValue.reg(anR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, an, IRValue.ofInt(0, IRType.I64), nv));
+ this.emit(IRInstr.mov(abs_n, an));
+ this.emit(IRInstr.label("its_pos"));
+
+ // Count digits
+ IRValue cnt = IRValue.reg("its_cnt", IRType.I64);
+ IRValue tmp = IRValue.reg("its_tmp", IRType.I64);
+ this.emit(IRInstr.mov(cnt, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.mov(tmp, abs_n));
+ this.emit(IRInstr.label("its_count"));
+ this.emit(IRInstr.cmp(tmp, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JLE, "its_counted"));
+ this.emit(IRInstr.binop(IROpcode.ADD, cnt, cnt, IRValue.ofInt(1, IRType.I64)));
+ String dvrR = this.newReg();
+ IRValue dvr = IRValue.reg(dvrR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.DIV, dvr, tmp, IRValue.ofInt(10, IRType.I64)));
+ this.emit(IRInstr.mov(tmp, dvr));
+ this.emit(IRInstr.jmp("its_count"));
+ this.emit(IRInstr.label("its_counted"));
+
+ // total = cnt + neg
+ String totR = this.newReg();
+ IRValue tot = IRValue.reg(totR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, tot, cnt, neg));
+ String aszR = this.newReg();
+ IRValue asz = IRValue.reg(aszR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, asz, tot, IRValue.ofInt(1, IRType.I64)));
+
+ IRValue buf = this.emitHeapAlloc(asz);
+
+ // null terminate
+ String nlPR = this.newReg();
+ IRValue nlP = IRValue.reg(nlPR, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, nlP, buf, tot));
+ this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), nlP, IRType.I8));
+
+ // write '-' if neg
+ this.emit(IRInstr.cmp(neg, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JE, "its_no_neg"));
+ String np0R = this.newReg();
+ IRValue np0 = IRValue.reg(np0R, IRType.PTR);
+ this.emit(IRInstr.mov(np0, buf));
+ this.emit(IRInstr.store(IRValue.ofInt(45, IRType.I64), np0, IRType.I8)); // '-'=45
+ this.emit(IRInstr.label("its_no_neg"));
+
+ // write digits right to left: pos = tot-1
+ IRValue pos = IRValue.reg("its_pos", IRType.I64);
+ IRValue abs2 = IRValue.reg("its_ab2", IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SUB, pos, tot, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.mov(abs2, abs_n));
+ this.emit(IRInstr.label("its_write"));
+ this.emit(IRInstr.cmp(abs2, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JLE, "its_written"));
+ String modR = this.newReg();
+ IRValue md = IRValue.reg(modR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.MOD, md, abs2, IRValue.ofInt(10, IRType.I64)));
+ String digR = this.newReg();
+ IRValue dig = IRValue.reg(digR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, dig, md, IRValue.ofInt(48, IRType.I64))); // '0'=48
+ String dPR = this.newReg();
+ IRValue dP = IRValue.reg(dPR, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, dP, buf, pos));
+ this.emit(IRInstr.store(dig, dP, IRType.I8));
+ String divR = this.newReg();
+ IRValue dv = IRValue.reg(divR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.DIV, dv, abs2, IRValue.ofInt(10, IRType.I64)));
+ this.emit(IRInstr.mov(abs2, dv));
+ this.emit(IRInstr.binop(IROpcode.SUB, pos, pos, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("its_write"));
+ this.emit(IRInstr.label("its_written"));
+ this.emit(IRInstr.ret(buf));
+ }
+
+ // ===== Entry point =====
+
+ private registerBssGlobal(name: String, size: Integer) : Integer {
+ Integer i = 0; while (i < this.bssNames.length()) {
+ if (this.bssNames.get(i) as String == name) { return i; } i = i + 1; }
+ this.bssNames.append(name); this.bssSizes.append(size);
+ return this.bssNames.length() - 1;
+ }
+
+ private generateStartFn() {
+ if (this.linux) { this.registerBssGlobal("__arimo_argc", 8); this.registerBssGlobal("__arimo_argv", 8); }
+ this.beginFn("_start", IRType.VOID); this.resetFnContext(); this.emit(IRInstr.label("entry"));
+ List mainArgs = List(); IRValue mainResult = IRValue.reg("main_result", IRType.I64);
+ if (this.mainFn != "") { this.emit(IRInstr.call(mainResult, this.mainFn, mainArgs)); }
+ else { this.emit(IRInstr.mov(mainResult, IRValue.ofInt(0, IRType.I64))); }
+ List exitArgs = List(); exitArgs.append(mainResult); IRValue exitDst = IRValue.reg("ex", IRType.VOID);
+ if (this.linux) { this.emit(IRInstr.syscall(exitDst, 60, exitArgs)); }
+ else { this.emit(IRInstr.call(exitDst, "__ext__ExitProcess", exitArgs)); }
+ this.emit(IRInstr.retVoid());
+ }
+
+ public lower(modules: List) {
+ this.registerClasses(modules);
+ this.generateTeardownRoutines();
+ this.internStr("\n");
+ Integer i = 0;
+ while (i < modules.length()) {
+ ArimoModule m = modules.get(i) as ArimoModule;
+ this.lowerModule(m);
+ i = i + 1;
+ }
+ this.generateStrlenHelper();
+ this.generateListNew();
+ this.generateListLength();
+ this.generateListGet();
+ this.generateListSet();
+ this.generateListAppend();
+ this.generateListRemoveAt();
+ this.generateStrCmp();
+ this.generateStrCat();
+ this.generateSubstr();
+ this.generateStartsWith();
+ this.generateEndsWith();
+ this.generateIndexOf();
+ this.generateCharCodeAt();
+ this.generateCharAt();
+ this.generateI64ToStr();
+ this.generatePrintlnInt("__arimo_println_int", true);
+ this.generatePrintlnInt("__arimo_print_int", false);
+ this.generatePrintHelper("__arimo_println", true);
+ this.generatePrintHelper("__arimo_print", false);
+ this.generateFopen();
+ this.generateToLower();
+ this.generateSystem();
+ this.generateBuildArgs();
+ this.generateStartFn();
+ }
+
+ private generateBuildArgs() {
+ this.beginFn("__arimo_build_args", IRType.PTR);
+ this.addParamToLast("argc", IRType.I64); this.addParamToLast("argvBase", IRType.PTR);
+ this.resetFnContext(); this.emit(IRInstr.label("entry"));
+ IRValue argcV = IRValue.reg("argc", IRType.I64); IRValue argvBaseV = IRValue.reg("argvBase", IRType.PTR);
+ String listReg = this.newReg(); IRValue listV = IRValue.reg(listReg, IRType.PTR);
+ List noArgs = List(); this.emit(IRInstr.call(listV, "__arimo_list_new", noArgs));
+ String iReg = this.newReg(); IRValue iV = IRValue.reg(iReg, IRType.I64);
+ this.emit(IRInstr.mov(iV, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("bargs_loop")); this.emit(IRInstr.cmp(iV, argcV));
+ this.emit(IRInstr.branch(IROpcode.JGE, "bargs_done"));
+ String offReg = this.newReg(); IRValue offV = IRValue.reg(offReg, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.SHL, offV, iV, IRValue.ofInt(3, IRType.I64)));
+ String slotReg = this.newReg(); IRValue slotV = IRValue.reg(slotReg, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, slotV, argvBaseV, offV));
+ String ptrReg = this.newReg(); IRValue ptrV = IRValue.reg(ptrReg, IRType.PTR);
+ this.emit(IRInstr.load(ptrV, slotV, IRType.PTR));
+ List appArgs = List(); appArgs.append(listV); appArgs.append(ptrV);
+ this.emit(IRInstr.call(IRValue.reg(this.newReg(), IRType.VOID), "__arimo_list_append", appArgs));
+ this.emit(IRInstr.binop(IROpcode.ADD, iV, iV, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("bargs_loop")); this.emit(IRInstr.label("bargs_done"));
+ this.emit(IRInstr.ret(listV));
+ }
+
+ private generateFopen() {
+ this.beginFn("__arimo_fopen", IRType.I64);
+ this.addParamToLast("path", IRType.PTR); this.addParamToLast("mode", IRType.PTR);
+ this.resetFnContext(); this.emit(IRInstr.label("entry"));
+ IRValue pathV = IRValue.reg("path", IRType.PTR); IRValue modeV = IRValue.reg("mode", IRType.PTR);
+ String mbyteR = this.newReg(); IRValue mbyte = IRValue.reg(mbyteR, IRType.I64);
+ this.emit(IRInstr.load(mbyte, modeV, IRType.I8));
+ String flagsR = this.newReg(); IRValue flags = IRValue.reg(flagsR, IRType.I64);
+ this.emit(IRInstr.cmp(mbyte, IRValue.ofInt(114, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JNE, "fo_nr")); this.emit(IRInstr.mov(flags, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.jmp("fo_op")); this.emit(IRInstr.label("fo_nr"));
+ this.emit(IRInstr.cmp(mbyte, IRValue.ofInt(97, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JNE, "fo_na"));
+ this.emit(IRInstr.mov(flags, IRValue.ofInt(1089, IRType.I64))); this.emit(IRInstr.jmp("fo_op"));
+ this.emit(IRInstr.label("fo_na")); this.emit(IRInstr.mov(flags, IRValue.ofInt(577, IRType.I64)));
+ this.emit(IRInstr.label("fo_op"));
+ String fdR = this.newReg(); IRValue fd = IRValue.reg(fdR, IRType.I64);
+ List openArgs = List(); openArgs.append(pathV); openArgs.append(flags);
+ openArgs.append(IRValue.ofInt(438, IRType.I64)); this.emit(IRInstr.syscall(fd, 2, openArgs));
+ String retR = this.newReg(); IRValue ret = IRValue.reg(retR, IRType.I64);
+ this.emit(IRInstr.cmp(fd, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JL, "fo_er")); this.emit(IRInstr.mov(ret, fd));
+ this.emit(IRInstr.jmp("fo_ok")); this.emit(IRInstr.label("fo_er"));
+ this.emit(IRInstr.mov(ret, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("fo_ok")); this.emit(IRInstr.ret(ret));
+ }
+
+ // generateToLower: ASCII lowercase helper — allocates new string, returns lowered copy.
+ // Input: PTR to null-terminated source string.
+ // Output: PTR to new null-terminated lowercase string (mmap-allocated).
+ private generateToLower() {
+ this.beginFn("__arimo_tolower", IRType.PTR);
+ this.addParamToLast("str", IRType.PTR);
+ this.resetFnContext(); this.emit(IRInstr.label("entry"));
+ IRValue strV = IRValue.reg("str", IRType.PTR);
+
+ // 1. Get string length via strlen
+ String lenR = this.newReg(); IRValue lenV = IRValue.reg(lenR, IRType.I64);
+ List slArgs = List(); slArgs.append(strV);
+ this.emit(IRInstr.call(lenV, "__arimo_strlen", slArgs));
+
+ // 2. Allocate len+1 bytes via mmap (syscall 9)
+ String sizeR = this.newReg(); IRValue sizeV = IRValue.reg(sizeR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, sizeV, lenV, IRValue.ofInt(1, IRType.I64)));
+ String bufR = this.newReg(); IRValue bufV = IRValue.reg(bufR, IRType.PTR);
+ List mmapArgs = List();
+ mmapArgs.append(IRValue.ofInt(0, IRType.I64)); // addr = NULL
+ mmapArgs.append(sizeV); // length
+ mmapArgs.append(IRValue.ofInt(3, IRType.I64)); // PROT_READ|PROT_WRITE
+ mmapArgs.append(IRValue.ofInt(34, IRType.I64)); // MAP_PRIVATE|MAP_ANONYMOUS
+ mmapArgs.append(IRValue.ofInt(-1, IRType.I64)); // fd = -1
+ mmapArgs.append(IRValue.ofInt(0, IRType.I64)); // offset = 0
+ this.emit(IRInstr.syscall(bufV, 9, mmapArgs));
+
+ // 3. Loop: copy chars, lowercasing A-Z → a-z
+ String iR = this.newReg(); IRValue iV = IRValue.reg(iR, IRType.I64);
+ this.emit(IRInstr.mov(iV, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.label("tl_loop"));
+ this.emit(IRInstr.cmp(iV, lenV));
+ this.emit(IRInstr.branch(IROpcode.JGE, "tl_done"));
+
+ // Load char from source[i]
+ String srcAddrR = this.newReg(); IRValue srcAddrV = IRValue.reg(srcAddrR, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, srcAddrV, strV, iV));
+ String chR = this.newReg(); IRValue chV = IRValue.reg(chR, IRType.I64);
+ this.emit(IRInstr.load(chV, srcAddrV, IRType.I8));
+
+ // If 'A' <= ch <= 'Z', add 32
+ this.emit(IRInstr.cmp(chV, IRValue.ofInt(65, IRType.I64))); // 'A'
+ this.emit(IRInstr.branch(IROpcode.JL, "tl_skip"));
+ this.emit(IRInstr.cmp(chV, IRValue.ofInt(90, IRType.I64))); // 'Z'
+ this.emit(IRInstr.branch(IROpcode.JG, "tl_skip"));
+ String loR = this.newReg(); IRValue loV = IRValue.reg(loR, IRType.I64);
+ this.emit(IRInstr.binop(IROpcode.ADD, loV, chV, IRValue.ofInt(32, IRType.I64)));
+ this.emit(IRInstr.mov(chV, loV));
+ this.emit(IRInstr.label("tl_skip"));
+
+ // Store to dest[i]
+ String dstAddrR = this.newReg(); IRValue dstAddrV = IRValue.reg(dstAddrR, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, dstAddrV, bufV, iV));
+ this.emit(IRInstr.store(chV, dstAddrV, IRType.I8));
+
+ // i++
+ this.emit(IRInstr.binop(IROpcode.ADD, iV, iV, IRValue.ofInt(1, IRType.I64)));
+ this.emit(IRInstr.jmp("tl_loop"));
+ this.emit(IRInstr.label("tl_done"));
+
+ // 4. Null-terminate
+ String nullAddrR = this.newReg(); IRValue nullAddrV = IRValue.reg(nullAddrR, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, nullAddrV, bufV, lenV));
+ this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I8), nullAddrV, IRType.I8));
+
+ // 5. Return new buffer
+ this.emit(IRInstr.ret(bufV));
+ }
+
+ // generateSystem: fork+execve+waitpid via Linux syscalls.
+ // system(cmd) — runs /bin/sh -c , returns exit status or -1 on error.
+ private generateSystem() {
+ this.beginFn("__arimo_system", IRType.I64);
+ this.addParamToLast("cmd", IRType.PTR);
+ this.resetFnContext(); this.emit(IRInstr.label("entry"));
+ IRValue cmdV = IRValue.reg("cmd", IRType.PTR);
+
+ // Allocate argv[4] array on heap (4 pointers = 32 bytes)
+ String argvR = this.newReg(); IRValue argvV = IRValue.reg(argvR, IRType.PTR);
+ List mmapA = List();
+ mmapA.append(IRValue.ofInt(0, IRType.I64)); // addr=NULL
+ mmapA.append(IRValue.ofInt(32, IRType.I64)); // 32 bytes
+ mmapA.append(IRValue.ofInt(3, IRType.I64)); // PROT_RW
+ mmapA.append(IRValue.ofInt(34, IRType.I64)); // MAP_PRIVATE|ANON
+ mmapA.append(IRValue.ofInt(-1, IRType.I64)); // fd=-1
+ mmapA.append(IRValue.ofInt(0, IRType.I64)); // offset=0
+ this.emit(IRInstr.syscall(argvV, 9, mmapA));
+
+ // argv[0] = "sh", argv[1] = "-c", argv[2] = cmd, argv[3] = NULL
+ String shStr = this.internStr("sh");
+ String dashC = this.internStr("-c");
+ String shPath = this.internStr("/bin/sh");
+ this.emit(IRInstr.store(IRValue.global(shStr), argvV, IRType.PTR));
+ String a1R = this.newReg(); IRValue a1V = IRValue.reg(a1R, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, a1V, argvV, IRValue.ofInt(8, IRType.I64)));
+ this.emit(IRInstr.store(IRValue.global(dashC), a1V, IRType.PTR));
+ String a2R = this.newReg(); IRValue a2V = IRValue.reg(a2R, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, a2V, argvV, IRValue.ofInt(16, IRType.I64)));
+ this.emit(IRInstr.store(cmdV, a2V, IRType.PTR));
+ String a3R = this.newReg(); IRValue a3V = IRValue.reg(a3R, IRType.PTR);
+ this.emit(IRInstr.binop(IROpcode.ADD, a3V, argvV, IRValue.ofInt(24, IRType.I64)));
+ this.emit(IRInstr.store(IRValue.ofInt(0, IRType.I64), a3V, IRType.PTR));
+
+ // fork() syscall 57
+ String pidR = this.newReg(); IRValue pidV = IRValue.reg(pidR, IRType.I64);
+ this.emit(IRInstr.syscall(pidV, 57, List()));
+ this.emit(IRInstr.cmp(pidV, IRValue.ofInt(0, IRType.I64)));
+ this.emit(IRInstr.branch(IROpcode.JL, "sy_err"));
+ this.emit(IRInstr.branch(IROpcode.JE, "sy_child"));
+
+ // Parent: wait4(pid, &status, 0, NULL) syscall 61
+ this.emit(IRInstr.label("sy_parent"));
+ String statR = this.newReg(); IRValue statV = IRValue.reg(statR, IRType.PTR);
+ this.emit(IRInstr.alloc(statV, 8));
+ List wArgs = List();
+ wArgs.append(pidV); wArgs.append(statV);
+ wArgs.append(IRValue.ofInt(0, IRType.I64));
+ wArgs.append(IRValue.ofInt(0, IRType.I64));
+ this.emit(IRInstr.syscall(IRValue.reg(this.newReg(), IRType.I64), 61, wArgs));
+ this.emit(IRInstr.ret(IRValue.ofInt(0, IRType.I64)));
+
+ // Child: execve("/bin/sh", argv, NULL) syscall 59
+ this.emit(IRInstr.label("sy_child"));
+ List exArgs = List();
+ exArgs.append(IRValue.global(shPath));
+ exArgs.append(argvV);
+ exArgs.append(IRValue.ofInt(0, IRType.I64));
+ this.emit(IRInstr.syscall(IRValue.reg(this.newReg(), IRType.I64), 59, exArgs));
+ // execve failed → fall through to error
+ this.emit(IRInstr.label("sy_err"));
+ this.emit(IRInstr.ret(IRValue.ofInt(-1, IRType.I64)));
+ }
+}
diff --git a/arimo/compiler/backend/IRToX64.arm b/arimo/compiler/backend/IRToX64.arm
index 3815a7b..f7cc980 100644
--- a/arimo/compiler/backend/IRToX64.arm
+++ b/arimo/compiler/backend/IRToX64.arm
@@ -21,6 +21,7 @@ package arimo.compiler.backend;
import arimo.compiler.backend.X64Reg;
import arimo.compiler.backend.X64Encoder;
import arimo.compiler.backend.RegAlloc;
+import arimo.compiler.backend.SafeRegAlloc;
import arimo.compiler.backend.IRFunction;
import arimo.compiler.backend.IRInstr;
import arimo.compiler.backend.IRValue;
@@ -34,15 +35,23 @@ public class IRToX64 {
private curName : String;
private frame : Integer;
private savedCount : Integer;
- private linux : Boolean;
+ private linux : Boolean;
+ private useSafeRegAlloc : Boolean;
+ private safeRa : SafeRegAlloc;
public constructor(enc: X64Encoder, ra: RegAlloc, linux: Boolean) {
- this.enc = enc;
- this.ra = ra;
- this.curName = "";
- this.frame = 0;
- this.savedCount = 0;
- this.linux = linux;
+ this.enc = enc;
+ this.ra = ra;
+ this.curName = "";
+ this.frame = 0;
+ this.savedCount = 0;
+ this.linux = linux;
+ this.useSafeRegAlloc = false;
+ }
+
+ public setSafeRegAlloc(sra: SafeRegAlloc) {
+ this.safeRa = sra;
+ this.useSafeRegAlloc = true;
}
private argRegs() : List {
@@ -144,6 +153,76 @@ public class IRToX64 {
}
}
+ // ===== SafeRegAlloc helpers (P4) =====
+
+ // safeLoadVal: load IR value into given scratch register from stack slot.
+ // Always stack-canonical — no physReg fast path.
+ private safeLoadVal(val: IRValue, scratchReg: Integer) : Integer {
+ if (val.kind == IRValueKind.IMM_INT) {
+ this.enc.movRI(scratchReg, val.immInt);
+ return scratchReg;
+ }
+ if (val.kind == IRValueKind.REG) {
+ this.safeRa.assertKnown(val.name);
+ this.safeRa.assertWritten(val.name);
+ Integer off = this.safeRa.slotOffsetByName(val.name);
+ this.enc.movRMem(scratchReg, X64Reg.RBP, off);
+ return scratchReg;
+ }
+ if (val.kind == IRValueKind.GLOBAL) {
+ this.enc.leaRip(scratchReg, val.name);
+ return scratchReg;
+ }
+ this.enc.xorZero(scratchReg);
+ return scratchReg;
+ }
+
+ // safeStoreDst: store result from scratch register to its canonical stack slot.
+ // Marks slot written. After this call, value is on stack and scratchReg is dead.
+ private safeStoreDst(name: String, srcReg: Integer) {
+ this.safeRa.assertKnown(name);
+ Integer off = this.safeRa.slotOffsetByName(name);
+ this.enc.movMemR(X64Reg.RBP, off, srcReg);
+ this.safeRa.markValueWritten(name);
+ }
+
+ // safeSlotOffset: delegate to SafeRegAlloc.slotOffsetByName.
+ private safeSlotOffset(name: String) : Integer {
+ return this.safeRa.slotOffsetByName(name);
+ }
+
+ // safeBeginInstr: clean scratch state before emitting an instruction.
+ // Checks invariant: no dirty scratch regs from previous instruction.
+ private safeBeginInstr() {
+ this.safeRa.assertClean();
+ this.safeRa.resetScratch();
+ }
+
+ // safeEndInstr: ensure scratch state is clean after instruction emission.
+ // Resets scratch cursor, then verifies invariant: dirty=false, sci=0.
+ private safeEndInstr() {
+ this.safeRa.resetScratch();
+ this.safeRa.assertClean();
+ }
+
+ // safeLoadArg: load IR value directly into a specific register.
+ // Does NOT use scratch pool — used for CALL/SYSCALL arg setup where
+ // the target register is an ABI arg reg that will be clobbered by the call.
+ private safeLoadArg(val: IRValue, targetReg: Integer) {
+ if (val.kind == IRValueKind.IMM_INT) {
+ this.enc.movRI(targetReg, val.immInt);
+ } else if (val.kind == IRValueKind.REG) {
+ this.safeRa.assertKnown(val.name);
+ this.safeRa.assertWritten(val.name);
+ Integer off = this.safeRa.slotOffsetByName(val.name);
+ this.enc.movRMem(targetReg, X64Reg.RBP, off);
+ } else if (val.kind == IRValueKind.GLOBAL) {
+ this.enc.leaRip(targetReg, val.name);
+ } else {
+ this.enc.xorZero(targetReg);
+ }
+ }
+
private emitBinop(op: Integer, instr: IRInstr) {
IRValue a = instr.operands.get(0) as IRValue;
IRValue b = instr.operands.get(1) as IRValue;
@@ -191,7 +270,83 @@ public class IRToX64 {
return used;
}
+ // ===== SafeRegAlloc emitFunction (P5) =====
+
+ private emitFunctionSafe(fn: IRFunction) {
+ this.safeRa.allocate(fn);
+ Integer frameSize = this.safeRa.frameSize();
+ this.frame = frameSize;
+ this.curName = fn.name;
+ this.savedCount = 0; // no callee-saved tracking in safe mode
+
+ this.enc.defineLabel(fn.name);
+
+ // _start raw prologue (Linux): capture argc/argv from initial stack
+ if (this.linux && fn.name == "_start") {
+ this.enc.movRMem(X64Reg.RAX, X64Reg.RSP, 0); // rax = [rsp] = argc
+ this.enc.leaRip(X64Reg.R11, "__arimo_argc");
+ this.enc.movMemR(X64Reg.R11, 0, X64Reg.RAX); // [__arimo_argc] = argc
+ this.enc.leaMem(X64Reg.R10, X64Reg.RSP, 8); // r10 = rsp + 8
+ this.enc.leaRip(X64Reg.R11, "__arimo_argv");
+ this.enc.movMemR(X64Reg.R11, 0, X64Reg.R10); // [__arimo_argv] = argv
+ }
+
+ if (!this.linux && fn.name == "_start") {
+ this.enc.subRI(X64Reg.RSP, 8);
+ }
+
+ // Standard prologue — no callee-saved register saves
+ this.enc.pushR(X64Reg.RBP);
+ this.enc.movRR(X64Reg.RBP, X64Reg.RSP);
+ if (frameSize > 0) {
+ this.enc.subRI(X64Reg.RSP, frameSize);
+ }
+
+ // Save incoming register parameters (0..5 on Linux, 0..3 on Windows)
+ List paramArgRegs = this.argRegs();
+ Integer pi = 0;
+ while (pi < fn.params.length() && pi < paramArgRegs.length()) {
+ IRParam param = fn.params.get(pi) as IRParam;
+ Integer argReg = paramArgRegs.get(pi) as Integer;
+ this.safeStoreDst(param.name, argReg);
+ pi = pi + 1;
+ }
+ // Save incoming stack parameters (beyond register count).
+ // Stack args are at [RBP+16], [RBP+24], ... (above return addr and saved RBP).
+ while (pi < fn.params.length()) {
+ IRParam param = fn.params.get(pi) as IRParam;
+ Integer stackOff = 16 + (pi - paramArgRegs.length()) * 8;
+ this.enc.movRMem(X64Reg.RAX, X64Reg.RBP, stackOff);
+ this.safeStoreDst(param.name, X64Reg.RAX);
+ pi = pi + 1;
+ }
+ this.safeRa.markAllParamsWritten(fn);
+
+ // Body: instruction emission loop
+ Integer i = 0;
+ while (i < fn.instrs.length()) {
+ this.safeBeginInstr();
+ this.emitInstrSafe(fn.instrs.get(i) as IRInstr);
+ this.safeEndInstr();
+ i = i + 1;
+ }
+
+ // Epilog — simple: no callee-saved restores
+ this.enc.defineLabel("${fn.name}__epilog");
+ if (frameSize > 0) {
+ this.enc.addRI(X64Reg.RSP, frameSize);
+ }
+ this.enc.popR(X64Reg.RBP);
+ this.enc.ret();
+ }
+
public emitFunction(fn: IRFunction) {
+ // ===== SafeRegAlloc mode (P5) =====
+ if (this.useSafeRegAlloc) {
+ this.emitFunctionSafe(fn);
+ return;
+ }
+
this.ra.allocate(fn);
Integer spillBytes = this.ra.totalSpillBytes() * 8;
@@ -213,6 +368,20 @@ public class IRToX64 {
this.savedCount = N;
this.enc.defineLabel(fn.name);
+
+ // _start raw prologue (Linux): capture argc/argv from initial stack
+ if (this.linux && fn.name == "_start") {
+ // [rsp] = argc, [rsp+8] = argv[0], [rsp+16] = argv[1], ...
+ // Read argc → @__arimo_argc
+ this.enc.movRMem(X64Reg.RAX, X64Reg.RSP, 0); // rax = [rsp] = argc
+ this.enc.leaRip(X64Reg.R11, "__arimo_argc");
+ this.enc.movMemR(X64Reg.R11, 0, X64Reg.RAX); // [__arimo_argc] = argc
+ // Read argv ptr → @__arimo_argv
+ this.enc.leaMem(X64Reg.R10, X64Reg.RSP, 8); // r10 = rsp + 8
+ this.enc.leaRip(X64Reg.R11, "__arimo_argv");
+ this.enc.movMemR(X64Reg.R11, 0, X64Reg.R10); // [__arimo_argv] = argv
+ }
+
if (!this.linux && fn.name == "_start") {
this.enc.subRI(X64Reg.RSP, 8);
}
@@ -247,6 +416,17 @@ public class IRToX64 {
Integer i = 0;
while (i < fn.instrs.length()) {
+ // Emit spill saves: store values whose regs were reassigned at this position
+ Integer si = 0;
+ while (si < this.ra.spillPoss.length()) {
+ if (this.ra.spillPoss.get(si) as Integer == i) {
+ Integer slot = this.ra.spillSaves.get(si) as Integer;
+ Integer preg = this.ra.spillRegs.get(si) as Integer;
+ this.enc.movMemR(X64Reg.RBP, this.spillOff(slot), preg);
+ }
+ si = si + 1;
+ }
+
IRInstr instr = fn.instrs.get(i) as IRInstr;
this.emitInstr(instr);
i = i + 1;
@@ -398,6 +578,11 @@ public class IRToX64 {
}
if (op == IROpcode.SYSCALL) {
+ // Save caller-saved regs used for syscall args (r8/r9/r10 may hold live IR values)
+ this.enc.pushR(X64Reg.R8);
+ this.enc.pushR(X64Reg.R9);
+ this.enc.pushR(X64Reg.R10);
+
Integer sysNum = instr.ty;
List scRegs = this.syscallArgRegs();
Integer sargc = instr.operands.length();
@@ -425,6 +610,11 @@ public class IRToX64 {
if (!instr.dst.isNone() && instr.dst.kind == IRValueKind.REG) {
this.storeDst(instr.dst.name, X64Reg.RAX);
}
+
+ // Restore caller-saved regs (reverse order)
+ this.enc.popR(X64Reg.R10);
+ this.enc.popR(X64Reg.R9);
+ this.enc.popR(X64Reg.R8);
return;
}
@@ -480,11 +670,311 @@ public class IRToX64 {
} else {
callTarget = instr.label;
}
+ // Save caller-saved regs that may hold live IR values (R8/R9 in alloc pool)
+ this.enc.pushR(X64Reg.R8);
+ this.enc.pushR(X64Reg.R9);
this.enc.callRel(callTarget);
+ this.enc.popR(X64Reg.R9);
+ this.enc.popR(X64Reg.R8);
if (!instr.dst.isNone() && instr.dst.kind == IRValueKind.REG) {
this.storeDst(instr.dst.name, X64Reg.RAX);
}
return;
}
}
+
+ // ===== SafeRegAlloc emitInstr (P6-A) =====
+
+ private emitInstrSafe(instr: IRInstr) {
+ Integer op = instr.op;
+
+ // --- Control flow (no register usage) ---
+
+ if (op == IROpcode.LABEL) { this.enc.defineLabel(instr.label); return; }
+ if (op == IROpcode.JMP) { this.enc.jmp(instr.label); return; }
+ if (op == IROpcode.JE) { this.enc.je(instr.label); return; }
+ if (op == IROpcode.JNE) { this.enc.jne(instr.label); return; }
+ if (op == IROpcode.JG) { this.enc.jg(instr.label); return; }
+ if (op == IROpcode.JL) { this.enc.jl(instr.label); return; }
+ if (op == IROpcode.JGE) { this.enc.jge(instr.label); return; }
+ if (op == IROpcode.JLE) { this.enc.jle(instr.label); return; }
+ if (op == IROpcode.JZ) { this.enc.jz(instr.label); return; }
+ if (op == IROpcode.JNZ) { this.enc.jnz(instr.label); return; }
+
+ // --- RET ---
+
+ if (op == IROpcode.RET) {
+ if (instr.operands.length() > 0) {
+ IRValue rv = instr.operands.get(0) as IRValue;
+ // Load return value directly into RAX (not in scratch pool)
+ if (rv.kind == IRValueKind.IMM_INT) {
+ this.enc.movRI(X64Reg.RAX, rv.immInt);
+ } else if (rv.kind == IRValueKind.REG) {
+ this.safeRa.assertKnown(rv.name);
+ Integer off = this.safeRa.slotOffsetByName(rv.name);
+ this.enc.movRMem(X64Reg.RAX, X64Reg.RBP, off);
+ } else if (rv.kind == IRValueKind.GLOBAL) {
+ this.enc.leaRip(X64Reg.RAX, rv.name);
+ } else {
+ this.enc.xorZero(X64Reg.RAX);
+ }
+ }
+ this.enc.jmp("${this.curName}__epilog");
+ return;
+ }
+
+ // --- MOV ---
+
+ if (op == IROpcode.MOV) {
+ Integer sr = this.safeRa.allocScratch();
+ IRValue src = instr.operands.get(0) as IRValue;
+ sr = this.safeLoadVal(src, sr);
+ this.safeStoreDst(instr.dst.name, sr);
+ return;
+ }
+
+ // --- Integer BINOP: ADD SUB MUL AND OR XOR ---
+
+ if (op == IROpcode.ADD || op == IROpcode.SUB ||
+ op == IROpcode.AND || op == IROpcode.OR || op == IROpcode.XOR ||
+ op == IROpcode.MUL) {
+ List regs = this.safeRa.allocScratch3();
+ IRValue a = instr.operands.get(0) as IRValue;
+ IRValue b = instr.operands.get(1) as IRValue;
+ Integer ra = this.safeLoadVal(a, regs.get(0) as Integer);
+ Integer rb = this.safeLoadVal(b, regs.get(1) as Integer);
+ Integer dr = regs.get(2) as Integer;
+ if (dr != ra) { this.enc.movRR(dr, ra); }
+ if (op == IROpcode.ADD) { this.enc.addRR(dr, rb); }
+ else if (op == IROpcode.SUB) { this.enc.subRR(dr, rb); }
+ else if (op == IROpcode.AND) { this.enc.andRR(dr, rb); }
+ else if (op == IROpcode.OR) { this.enc.orRR(dr, rb); }
+ else if (op == IROpcode.XOR) { this.enc.xorRR(dr, rb); }
+ else if (op == IROpcode.MUL) { this.enc.imulRR(dr, rb); }
+ this.safeStoreDst(instr.dst.name, dr);
+ return;
+ }
+
+ // --- DIV / MOD (uses RAX:RDX implicitly, not in scratch pool) ---
+
+ if (op == IROpcode.DIV || op == IROpcode.MOD) {
+ List regs = this.safeRa.allocScratch2();
+ IRValue a = instr.operands.get(0) as IRValue;
+ IRValue b = instr.operands.get(1) as IRValue;
+ // Load dividend into a scratch, then move to RAX
+ Integer ra = this.safeLoadVal(a, regs.get(0) as Integer);
+ this.enc.movRR(X64Reg.RAX, ra);
+ this.enc.cqo();
+ // Load divisor into scratch, use as idiv operand
+ Integer rb = this.safeLoadVal(b, regs.get(1) as Integer);
+ this.enc.idivR(rb);
+ if (op == IROpcode.DIV) {
+ this.safeStoreDst(instr.dst.name, X64Reg.RAX);
+ } else {
+ this.safeStoreDst(instr.dst.name, X64Reg.RDX);
+ }
+ return;
+ }
+
+ // --- SHL / SHR (shift amount must be in CL; use RAX as temp) ---
+
+ if (op == IROpcode.SHL || op == IROpcode.SHR) {
+ List regs = this.safeRa.allocScratch2();
+ IRValue a = instr.operands.get(0) as IRValue;
+ IRValue b = instr.operands.get(1) as IRValue;
+ // Load value into scratch, then move to RAX (safe temp)
+ Integer ra = this.safeLoadVal(a, regs.get(0) as Integer);
+ this.enc.movRR(X64Reg.RAX, ra);
+ // Load shift amount, move to CL (RCX)
+ Integer rb = this.safeLoadVal(b, regs.get(1) as Integer);
+ if (rb != X64Reg.RCX) { this.enc.movRR(X64Reg.RCX, rb); }
+ if (op == IROpcode.SHL) { this.enc.shlRCL(X64Reg.RAX); }
+ else { this.enc.shrRCL(X64Reg.RAX); }
+ this.safeStoreDst(instr.dst.name, X64Reg.RAX);
+ return;
+ }
+
+ // --- NEG ---
+
+ if (op == IROpcode.NEG) {
+ Integer sr = this.safeRa.allocScratch();
+ IRValue src = instr.operands.get(0) as IRValue;
+ sr = this.safeLoadVal(src, sr);
+ this.enc.negR(sr);
+ this.safeStoreDst(instr.dst.name, sr);
+ return;
+ }
+
+ // --- NOT ---
+
+ if (op == IROpcode.NOT) {
+ Integer sr = this.safeRa.allocScratch();
+ IRValue src = instr.operands.get(0) as IRValue;
+ sr = this.safeLoadVal(src, sr);
+ this.enc.notR(sr);
+ this.safeStoreDst(instr.dst.name, sr);
+ return;
+ }
+
+ // --- CMP ---
+
+ if (op == IROpcode.CMP) {
+ List regs = this.safeRa.allocScratch2();
+ IRValue a = instr.operands.get(0) as IRValue;
+ IRValue b = instr.operands.get(1) as IRValue;
+ Integer ra = this.safeLoadVal(a, regs.get(0) as Integer);
+ Integer rb = this.safeLoadVal(b, regs.get(1) as Integer);
+ this.enc.cmpRR(ra, rb);
+ // CMP has no dst — flags are set, consumed by following branch
+ return;
+ }
+
+ // --- ALLOC (stack allocation pointer) ---
+
+ if (op == IROpcode.ALLOC) {
+ Integer sr = this.safeRa.allocScratch();
+ this.enc.leaMem(sr, X64Reg.RSP, 0);
+ this.safeStoreDst(instr.dst.name, sr);
+ return;
+ }
+
+ // --- LOAD (memory read) ---
+
+ if (op == IROpcode.LOAD) {
+ List regs = this.safeRa.allocScratch2();
+ IRValue ptr = instr.operands.get(0) as IRValue;
+ Integer pr = this.safeLoadVal(ptr, regs.get(0) as Integer);
+ Integer dr = regs.get(1) as Integer;
+ if (instr.ty == IRType.I8) {
+ this.enc.movzxByteMem(dr, pr);
+ } else {
+ this.enc.movRMemBase(dr, pr);
+ }
+ this.safeStoreDst(instr.dst.name, dr);
+ return;
+ }
+
+ // --- STORE (memory write) ---
+
+ if (op == IROpcode.STORE) {
+ List regs = this.safeRa.allocScratch2();
+ IRValue val = instr.operands.get(0) as IRValue;
+ IRValue ptr = instr.operands.get(1) as IRValue;
+ Integer vr = this.safeLoadVal(val, regs.get(0) as Integer);
+ Integer pr = this.safeLoadVal(ptr, regs.get(1) as Integer);
+ if (instr.ty == IRType.I8) {
+ this.enc.movByteMemR(pr, vr);
+ } else {
+ this.enc.movMemBaseR(pr, vr);
+ }
+ // STORE has no dst — value written to memory
+ return;
+ }
+
+ // --- Type conversions: ITOP PTOI ZEXT SEXT TRUNC ---
+
+ if (op == IROpcode.ITOP || op == IROpcode.PTOI ||
+ op == IROpcode.ZEXT || op == IROpcode.SEXT || op == IROpcode.TRUNC) {
+ Integer sr = this.safeRa.allocScratch();
+ IRValue src = instr.operands.get(0) as IRValue;
+ sr = this.safeLoadVal(src, sr);
+ this.safeStoreDst(instr.dst.name, sr);
+ return;
+ }
+
+ // --- SYSCALL ---
+
+ if (op == IROpcode.SYSCALL) {
+ Integer sysNum = instr.ty;
+ List scRegs = this.syscallArgRegs();
+ Integer sargc = instr.operands.length();
+ Integer si = 0;
+ while (si < sargc && si < scRegs.length()) {
+ IRValue sarg = instr.operands.get(si) as IRValue;
+ Integer sreg = scRegs.get(si) as Integer;
+ this.safeLoadArg(sarg, sreg);
+ si = si + 1;
+ }
+ if (sargc > scRegs.length()) {
+ this.enc.hasError = true;
+ IO.println("arc: [FAIL] SafeRegAlloc: SYSCALL too many args (${sargc}) in fn ${this.curName}");
+ }
+ this.enc.movRI(X64Reg.RAX, sysNum);
+ this.enc.syscall();
+ if (!instr.dst.isNone() && instr.dst.kind == IRValueKind.REG) {
+ this.safeStoreDst(instr.dst.name, X64Reg.RAX);
+ }
+ return;
+ }
+
+ // --- CALL ---
+
+ if (op == IROpcode.CALL) {
+ List argRegsLocal = this.argRegs();
+ Integer argc = instr.operands.length();
+ Integer ai = 0;
+ while (ai < argc && ai < argRegsLocal.length()) {
+ IRValue arg = instr.operands.get(ai) as IRValue;
+ Integer areg = argRegsLocal.get(ai) as Integer;
+ this.safeLoadArg(arg, areg);
+ ai = ai + 1;
+ }
+ // Stack args (>reg count): write to [RSP+off] and align RSP to 16 before CALL.
+ // Linux ABI: args 7+ at [RSP+0],[RSP+8],... before call (→ [RSP+8],[RSP+16],... after).
+ Integer stackArgStart = argRegsLocal.length();
+ Integer stackArgCount = argc - stackArgStart;
+ Integer stackBytes = 0;
+ Integer padBytes = 0;
+ if (stackArgCount > 0) {
+ stackBytes = stackArgCount * 8;
+ if (stackBytes % 16 != 0) { padBytes = 8; }
+ Integer totalSub = stackBytes + padBytes;
+ if (totalSub > 0) { this.enc.subRI(X64Reg.RSP, totalSub); }
+ Integer si = 0;
+ while (si < stackArgCount) {
+ IRValue sarg = instr.operands.get(stackArgStart + si) as IRValue;
+ Integer soff = si * 8;
+ if (sarg.kind == IRValueKind.IMM_INT) {
+ this.enc.movRI(X64Reg.RAX, sarg.immInt);
+ this.enc.movMemR(X64Reg.RSP, soff, X64Reg.RAX);
+ } else if (sarg.kind == IRValueKind.REG) {
+ this.safeRa.assertKnown(sarg.name);
+ Integer slOff = this.safeRa.slotOffsetByName(sarg.name);
+ this.enc.movRMem(X64Reg.RAX, X64Reg.RBP, slOff);
+ this.enc.movMemR(X64Reg.RSP, soff, X64Reg.RAX);
+ } else if (sarg.kind == IRValueKind.GLOBAL) {
+ this.enc.leaRip(X64Reg.RAX, sarg.name);
+ this.enc.movMemR(X64Reg.RSP, soff, X64Reg.RAX);
+ }
+ si = si + 1;
+ }
+ }
+ String callTarget = "";
+ if (instr.label.startsWith("__ext__")) {
+ String extName = instr.label.substring(7, instr.label.length());
+ if (this.linux) {
+ callTarget = extName;
+ } else {
+ callTarget = "${extName}__thunk";
+ }
+ } else {
+ callTarget = instr.label;
+ }
+ // In safe mode, no live IR values in registers — no R8/R9 push/pop needed
+ this.enc.callRel(callTarget);
+ // Clean up stack args after call
+ if (stackBytes + padBytes > 0) {
+ this.enc.addRI(X64Reg.RSP, stackBytes + padBytes);
+ }
+ if (!instr.dst.isNone() && instr.dst.kind == IRValueKind.REG) {
+ this.safeStoreDst(instr.dst.name, X64Reg.RAX);
+ }
+ return;
+ }
+
+ // --- Unsupported opcode in safe mode ---
+
+ this.enc.hasError = true;
+ IO.println("arc: [FAIL] SafeRegAlloc: unsupported opcode ${IROpcode.name(op)} (${op}) in fn ${this.curName}");
+ }
}
diff --git a/arimo/compiler/backend/NativeBackend.arm b/arimo/compiler/backend/NativeBackend.arm
index 0aaee4e..60a557d 100644
--- a/arimo/compiler/backend/NativeBackend.arm
+++ b/arimo/compiler/backend/NativeBackend.arm
@@ -23,6 +23,7 @@ import arimo.compiler.backend.IRLower;
import arimo.compiler.backend.IRToX64;
import arimo.compiler.backend.IRFunction;
import arimo.compiler.backend.RegAlloc;
+import arimo.compiler.backend.SafeRegAlloc;
import arimo.compiler.backend.X64Encoder;
import arimo.compiler.backend.PEWriter;
import arimo.compiler.backend.ELFWriter;
@@ -33,20 +34,26 @@ public class NativeBackend {
private ra : RegAlloc;
private pe : PEWriter;
private elf : ELFWriter;
- private linux : Boolean;
+ private linux : Boolean;
+ private useSafeRegAlloc : Boolean;
public constructor(linux: Boolean) {
- this.linux = linux;
- this.lowerer = IRLower(linux);
- this.enc = X64Encoder();
- this.ra = RegAlloc();
- this.pe = PEWriter(this.enc);
- this.elf = ELFWriter(this.enc);
+ this.linux = linux;
+ this.lowerer = IRLower(linux);
+ this.enc = X64Encoder();
+ this.ra = RegAlloc();
+ this.pe = PEWriter(this.enc);
+ this.elf = ELFWriter(this.enc);
+ this.useSafeRegAlloc = false;
if (!linux) {
this.setupImports();
}
}
+ public setSafeRegAlloc(enabled: Boolean) {
+ this.useSafeRegAlloc = enabled;
+ }
+
private setupImports() {
this.pe.addImport("ExitProcess");
this.pe.addImport("GetStdHandle");
@@ -61,6 +68,10 @@ public class NativeBackend {
this.lowerer.lower(modules);
IRToX64 irx = IRToX64(this.enc, this.ra, this.linux);
+ if (this.useSafeRegAlloc) {
+ SafeRegAlloc sra = SafeRegAlloc();
+ irx.setSafeRegAlloc(sra);
+ }
Integer entryOff = 0;
Integer fi = 0;
@@ -71,6 +82,11 @@ public class NativeBackend {
fi = fi + 1;
}
+ if (this.enc.hasError) {
+ IO.println("arc: build failed — errors detected during code generation");
+ return;
+ }
+
Integer si = 0;
if (this.linux) {
while (si < this.lowerer.strNames.length()) {
@@ -79,7 +95,18 @@ public class NativeBackend {
this.elf.addString(nm, ct);
si = si + 1;
}
+ Integer bi = 0;
+ while (bi < this.lowerer.bssNames.length()) {
+ String bnm = this.lowerer.bssNames.get(bi) as String;
+ Integer bsz = this.lowerer.bssSizes.get(bi) as Integer;
+ this.elf.addBss(bnm, bsz);
+ bi = bi + 1;
+ }
List elfBytes = this.elf.build(entryOff);
+ if (this.enc.hasError) {
+ IO.println("arc: build failed — unresolved labels or fixup errors");
+ return;
+ }
this.elf.writeTo(outPath, elfBytes);
} else {
while (si < this.lowerer.strNames.length()) {
@@ -89,6 +116,10 @@ public class NativeBackend {
si = si + 1;
}
List peBytes = this.pe.build(entryOff);
+ if (this.enc.hasError) {
+ IO.println("arc: build failed — unresolved labels or fixup errors");
+ return;
+ }
this.pe.writeTo(outPath, peBytes);
}
}