diff --git a/Lang/src/main/java/chipmunk/compiler/assembler/HVMAssembler.java b/Lang/src/main/java/chipmunk/compiler/assembler/HVMAssembler.java index 0a0378e..7e5a496 100644 --- a/Lang/src/main/java/chipmunk/compiler/assembler/HVMAssembler.java +++ b/Lang/src/main/java/chipmunk/compiler/assembler/HVMAssembler.java @@ -1,56 +1,172 @@ package chipmunk.compiler.assembler; import myworld.hummingbird.Executable; -import myworld.hummingbird.Opcode; import myworld.hummingbird.Opcodes; -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; +import java.nio.charset.StandardCharsets; + +import static chipmunk.compiler.assembler.HVMType.*; public class HVMAssembler { - private final Stack types = new Stack<>(); - private final RegisterAllocator registers = new RegisterAllocator(); + private final Operands operands = new Operands(); protected final Executable.Builder builder = new Executable.Builder(); public void push(Object value){ + Operand op = null; if(value instanceof Integer || value instanceof Long){ - types.push(HVMType.LONG); + op = operands.push(LONG); }else if(value instanceof Float || value instanceof Double){ - types.push(HVMType.DOUBLE); + op = operands.push(DOUBLE); }else if(value instanceof String){ - types.push(HVMType.STRING_REF); + op = operands.push(STRING_REF); }else{ // TODO - invalid constant type } - // TODO - Strings have to be encoded in the data section, with the address pushed as a constant. - builder.appendOpcode(Opcodes.CONST(0, value)); // TODO - register allocation + if(op.type() == HVMType.STRING_REF){ + // Strings have to be encoded in the data section, with the address pushed as a constant. + value = builder.appendData(value.toString().getBytes(StandardCharsets.UTF_8)); + } + builder.appendOpcode(Opcodes.CONST(op.register(), value)); } public void add(){ - // TODO - for each Chipmunk opcode we have to inspect the HVM operand types and select the - // opcode to use, possibly emitting conversion opcodes (converting longs to doubles, for example). - var a = types.pop(); - var b = types.pop(); + var b = operands.pop(); + var a = operands.pop(); + + if(a != b){ + promoteType(a, b); + } + + switch (a.type()){ + case LONG -> builder.appendOpcode(Opcodes.ADD(a.register(), a.register(), b.register())); + case DOUBLE -> builder.appendOpcode(Opcodes.DADD(a.register(), a.register(), b.register())); + } + } + + public void sub(){ + var b = operands.pop(); + var a = operands.pop(); + + if(a != b){ + promoteType(a, b); + } + + switch (a.type()){ + case LONG -> builder.appendOpcode(Opcodes.SUB(a.register(), a.register(), b.register())); + case DOUBLE -> builder.appendOpcode(Opcodes.DSUB(a.register(), a.register(), b.register())); + } + } + + public void mul(){ + var b = operands.pop(); + var a = operands.pop(); + if(a != b){ promoteType(a, b); } - switch (a){ - case LONG -> builder.appendOpcode(Opcodes.ADD(registers.pushRegister(), 0, 0)); - case DOUBLE -> builder.appendOpcode(Opcodes.DADD(registers.pushRegister(), 0, 0)); + switch (a.type()){ + case LONG -> builder.appendOpcode(Opcodes.MUL(a.register(), a.register(), b.register())); + case DOUBLE -> builder.appendOpcode(Opcodes.DMUL(a.register(), a.register(), b.register())); + } + } + + public void div(){ + var b = operands.pop(); + var a = operands.pop(); + + if(a != b){ + promoteType(a, b); + } + + switch (a.type()){ + case LONG -> builder.appendOpcode(Opcodes.DIV(a.register(), a.register(), b.register())); + case DOUBLE -> builder.appendOpcode(Opcodes.DDIV(a.register(), a.register(), b.register())); + } + } + + public void fdiv(){ + var b = operands.pop(); + var a = operands.pop(); + + if(a != b){ + promoteType(a, b); + } + + // TODO - floor division + switch (a.type()){ + case LONG -> builder.appendOpcode(Opcodes.DIV(a.register(), a.register(), b.register())); + case DOUBLE -> builder.appendOpcode(Opcodes.DDIV(a.register(), a.register(), b.register())); + } + } + + public void mod(){ + var b = operands.pop(); + var a = operands.pop(); + + if(a != b){ + promoteType(a, b); + } + + switch (a.type()){ + case LONG -> builder.appendOpcode(Opcodes.REM(a.register(), a.register(), b.register())); + case DOUBLE -> builder.appendOpcode(Opcodes.DREM(a.register(), a.register(), b.register())); + } + } + + public void pow(){ + var b = operands.pop(); + var a = operands.pop(); + + if(a != b){ + promoteType(a, b); + } + + switch (a.type()){ + case LONG -> builder.appendOpcode(Opcodes.POW(a.register(), a.register(), b.register())); + case DOUBLE -> builder.appendOpcode(Opcodes.DPOW(a.register(), a.register(), b.register())); + } + } + + public void inc(){ + var a = operands.pop(); + + switch (a.type()){ + case LONG -> { + builder.appendOpcode(Opcodes.CONST(a.register() + 1, 1)); + builder.appendOpcode(Opcodes.ADD(a.register(), a.register(), a.register() + 1)); + } + case DOUBLE -> { + builder.appendOpcode(Opcodes.CONST(a.register() + 1, 1.0d)); + builder.appendOpcode(Opcodes.DADD(a.register(), a.register(), a.register() + 1)); + } + } + } + + public void dec(){ + var a = operands.pop(); + + switch (a.type()){ + case LONG -> { + builder.appendOpcode(Opcodes.CONST(a.register() + 1, 1)); + builder.appendOpcode(Opcodes.SUB(a.register(), a.register(), a.register() + 1)); + } + case DOUBLE -> { + builder.appendOpcode(Opcodes.CONST(a.register() + 1, 1.0d)); + builder.appendOpcode(Opcodes.DSUB(a.register(), a.register(), a.register() + 1)); + } } } - protected void promoteType(HVMType a, HVMType b){ - if(a == HVMType.LONG && b == HVMType.DOUBLE){ - // TODO - promote register with a - builder.appendOpcode(Opcodes.L2D(0, 0)); - }else if(a == HVMType.DOUBLE && b == HVMType.LONG){ - // TODO - promote register with b - builder.appendOpcode(Opcodes.L2D(0, 0)); + protected void promoteType(Operand a, Operand b){ + if(a.type() == LONG && b.type() == DOUBLE){ + // Promote register with a + builder.appendOpcode(Opcodes.L2D(a.register(), a.register())); + }else if(a.type() == DOUBLE && b.type() == LONG){ + // Promote register with b + builder.appendOpcode(Opcodes.L2D(b.register(), b.register())); } } diff --git a/Lang/src/main/java/chipmunk/compiler/assembler/Operand.java b/Lang/src/main/java/chipmunk/compiler/assembler/Operand.java new file mode 100644 index 0000000..0ca439a --- /dev/null +++ b/Lang/src/main/java/chipmunk/compiler/assembler/Operand.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 MyWorld, LLC + * All rights reserved. + * + * This file is part of Chipmunk. + * + * Chipmunk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chipmunk 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chipmunk. If not, see . + */ + +package chipmunk.compiler.assembler; + +public record Operand(int register, HVMType type) {} diff --git a/Lang/src/main/java/chipmunk/compiler/assembler/Operands.java b/Lang/src/main/java/chipmunk/compiler/assembler/Operands.java new file mode 100644 index 0000000..4ce3604 --- /dev/null +++ b/Lang/src/main/java/chipmunk/compiler/assembler/Operands.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 MyWorld, LLC + * All rights reserved. + * + * This file is part of Chipmunk. + * + * Chipmunk is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chipmunk 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chipmunk. If not, see . + */ + +package chipmunk.compiler.assembler; + +import java.util.Stack; + +public class Operands { + + protected Stack types = new Stack<>(); + + public Operand push(HVMType type){ + types.push(type); + return new Operand(types.size() - 1, type); + } + + public Operand pop(){ + return new Operand(types.size() - 1, types.pop()); + } + +} diff --git a/Lang/src/main/java/chipmunk/compiler/assembler/RegisterAllocator.java b/Lang/src/main/java/chipmunk/compiler/assembler/RegisterAllocator.java deleted file mode 100644 index 4002b7e..0000000 --- a/Lang/src/main/java/chipmunk/compiler/assembler/RegisterAllocator.java +++ /dev/null @@ -1,16 +0,0 @@ -package chipmunk.compiler.assembler; - -// TODO - support permanently reserving registers (such as for constants) -public class RegisterAllocator { - private int register; - - public int pushRegister(){ - var r = register; - register++; - return r; - } - - public void popRegister(){ - register--; - } -}