Skip to content

Commit

Permalink
Add (dumb) x86-64 code generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
grahamedgecombe committed Apr 12, 2013
1 parent 8eed083 commit e299956
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 8 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
.*
*~
*.iml
*.o
*.asm
/target
!.git*
!.mailmap
6 changes: 6 additions & 0 deletions examples/double.tb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
10 INPUT X
20 GOSUB 50
30 PRINT X
40 END
50 LET X = X * 2
60 RETURN
17 changes: 10 additions & 7 deletions src/main/java/com/grahamedgecombe/tinybasic/TinyBasicCompiler.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.grahamedgecombe.tinybasic;

import com.grahamedgecombe.tinybasic.ast.Program;
import com.grahamedgecombe.tinybasic.codegen.CodeGenerator;
import com.grahamedgecombe.tinybasic.codegen.x86_64.X86_64CodeGenerator;
import com.grahamedgecombe.tinybasic.parser.Parser;
import com.grahamedgecombe.tinybasic.stackir.Instruction;
import com.grahamedgecombe.tinybasic.stackir.InstructionSequence;
import com.grahamedgecombe.tinybasic.tokenizer.Tokenizer;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -15,15 +17,16 @@
public final class TinyBasicCompiler {

public static void main(String[] args) throws IOException {
Path path = Paths.get(args[0]);
try (Tokenizer tokenizer = new Tokenizer(Files.newBufferedReader(path, StandardCharsets.UTF_8))) {
Path inputPath = Paths.get(args[0]);
try (Tokenizer tokenizer = new Tokenizer(Files.newBufferedReader(inputPath, StandardCharsets.UTF_8))) {
try (Parser parser = new Parser(tokenizer)) {
Program program = parser.parse();
InstructionSequence seq = new InstructionSequence();
program.compile(seq);

for (Instruction instruction : seq.getInstructions())
System.out.println(instruction);
InstructionSequence seq = program.compile();

try (CodeGenerator generator = new X86_64CodeGenerator(new OutputStreamWriter(System.out))) {
generator.generate(seq);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public void compile(InstructionSequence seq) {
/* this is rather hacky, but the only place types are important, so it doesn't seem worth improving it */
seq.append(new Instruction(value instanceof ImmediateString ? Opcode.OUTS : Opcode.OUTI));
}

seq.append(new Instruction(Opcode.PUSHS, "\n"), new Instruction(Opcode.OUTS));
}

}
4 changes: 3 additions & 1 deletion src/main/java/com/grahamedgecombe/tinybasic/ast/Program.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ public String toString() {
return buf.toString();
}

public void compile(InstructionSequence seq) {
public InstructionSequence compile() {
InstructionSequence seq = new InstructionSequence();
for (Line line : lines) {
line.compile(seq);
}
return seq;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.grahamedgecombe.tinybasic.codegen;

import com.grahamedgecombe.tinybasic.stackir.InstructionSequence;

import java.io.Closeable;
import java.io.IOException;

public abstract class CodeGenerator implements Closeable {

public abstract void generate(InstructionSequence seq) throws IOException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package com.grahamedgecombe.tinybasic.codegen.x86_64;

import com.grahamedgecombe.tinybasic.codegen.CodeGenerator;
import com.grahamedgecombe.tinybasic.stackir.Instruction;
import com.grahamedgecombe.tinybasic.stackir.InstructionSequence;

import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

public final class X86_64CodeGenerator extends CodeGenerator {

private final Writer writer;

public X86_64CodeGenerator(Writer writer) {
this.writer = writer;
}

@Override
public void generate(InstructionSequence seq) throws IOException {
writer.append("[extern exit]\n");
writer.append("[extern printf]\n");
writer.append("[extern scanf]\n");
writer.append("[section .code]\n");
writer.append("[global main]\n");
writer.append("main:\n");
writer.append(" push rbp\n");
writer.append(" mov rbp, rsp\n");
writer.append(" sub rsp, " + (8 * 27) + "\n");

Map<String, String> strings = new HashMap<>();
for (Instruction instruction : seq.getInstructions()) {
switch (instruction.getOpcode()) {
case LABEL:
writer.append(instruction.getStringOperand().get() + ":\n");
break;

case PUSHI:
writer.append(" push 0x" + Integer.toHexString(instruction.getIntegerOperand().get()) + "\n");
break;

case PUSHS:
String label = seq.createGeneratedLabel();
strings.put(label, instruction.getStringOperand().get());
writer.append(" push " + label + "\n");
break;

case LOAD:
writer.append(" mov rax, [rbp - " + varIndex(instruction) + "]\n");
writer.append(" push rax\n");
break;

case STORE:
writer.append(" pop rax\n");
writer.append(" mov [rbp - " + varIndex(instruction) + "], rax\n");
break;

case ADD:
writer.append(" pop rax\n");
writer.append(" pop rbx\n");
writer.append(" add rax, rbx\n");
writer.append(" push rax\n");
break;

case SUB:
writer.append(" pop rax\n");
writer.append(" pop rbx\n");
writer.append(" sub rax, rbx\n");
writer.append(" push rax\n");
break;

case MUL:
writer.append(" pop rax\n");
writer.append(" pop rbx\n");
writer.append(" imul rax, rbx\n");
writer.append(" push rax\n");
break;

case DIV:
writer.append(" xor rdx, rdx\n");
writer.append(" pop rax\n");
writer.append(" pop rbx\n");
writer.append(" idiv rbx\n");
writer.append(" push rax\n");
break;

case CALL:
writer.append(" call " + instruction.getStringOperand().get() + "\n");
break;

case RET:
writer.append(" ret\n");
break;

case JMP:
writer.append(" jmp " + instruction.getStringOperand().get() + "\n");
break;

case JMPGT:
writer.append(" pop rbx\n");
writer.append(" pop rax\n");
writer.append(" cmp rax, rbx\n");
writer.append(" jg " + instruction.getStringOperand().get() + "\n");
break;

case JMPGTE:
writer.append(" pop rbx\n");
writer.append(" pop rax\n");
writer.append(" cmp rax, rbx\n");
writer.append(" jge " + instruction.getStringOperand().get() + "\n");
break;

case JMPLT:
writer.append(" pop rbx\n");
writer.append(" pop rax\n");
writer.append(" cmp rax, rbx\n");
writer.append(" jl " + instruction.getStringOperand().get() + "\n");
break;

case JMPLTE:
writer.append(" pop rbx\n");
writer.append(" pop rax\n");
writer.append(" cmp rax, rbx\n");
writer.append(" jle " + instruction.getStringOperand().get() + "\n");
break;

case JMPEQ:
writer.append(" pop rbx\n");
writer.append(" pop rax\n");
writer.append(" cmp rax, rbx\n");
writer.append(" je " + instruction.getStringOperand().get() + "\n");
break;

case JMPNE:
writer.append(" pop rbx\n");
writer.append(" pop rax\n");
writer.append(" cmp rax, rbx\n");
writer.append(" jne " + instruction.getStringOperand().get() + "\n");
break;

case HLT:
writer.append(" mov rdi, 0\n");
writer.append(" call exit\n");
break;

case IN:
strings.put("num_fmt", "%d");
writer.append(" lea rsi, [rbp - " + (8 * 26) + "]\n");
writer.append(" mov rdi, num_fmt\n");
writer.append(" mov al, 0\n");
writer.append(" call scanf\n");
writer.append(" xor rax, rax\n");
writer.append(" mov eax, [rbp - " + (8 * 26) + "]\n");
writer.append(" push rax\n");
break;

case OUTS:
strings.put("str_fmt", "%s");
writer.append(" pop rsi\n");
writer.append(" mov rdi, str_fmt\n");
writer.append(" mov al, 0\n");
writer.append(" call printf\n");
break;

case OUTI:
strings.put("num_fmt", "%d");
writer.append(" pop rsi\n");
writer.append(" mov rdi, num_fmt\n");
writer.append(" mov al, 0\n");
writer.append(" call printf\n");
break;
}
}

writer.append(" mov rax, 0\n");
writer.append(" mov rsp, rbp\n");
writer.append(" pop rbp\n");
writer.append(" ret\n");

writer.append("[section .rodata]\n");
for (Map.Entry<String, String> string : strings.entrySet()) {
writer.append(string.getKey() + ":\n");
writer.append(" db \"" + escape(string.getValue()) + "\", 0\n");
}
}

private String escape(String value) {
value = value.replace("\n", "\", 10, \"");
return value;
}

private int varIndex(Instruction instruction) {
return (instruction.getStringOperand().get().charAt(0) - 'A') * 8;
}

@Override
public void close() throws IOException {
writer.close();
}

}

0 comments on commit e299956

Please sign in to comment.