summaryrefslogtreecommitdiff
path: root/src/main/java/no/eliashaugsbakk/kompilator/asmGeneration/AssemblyBuilder.java
blob: 275a6836f041cb55f594abd9b69fbda200674866 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package no.eliashaugsbakk.kompilator.asmGeneration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Alloc;
import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Assign;
import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Call;
import no.eliashaugsbakk.kompilator.IRGeneration.Instructions.Instruction;

public class AssemblyBuilder {
  Map<String, StringVar> stringVariables = new HashMap<>();

  record StringVar(String value, boolean mutable) {
  }

  StringBuilder finalAssembly;
  StringBuilder text;
  StringBuilder rodata;
  StringBuilder data;
  StringBuilder bss;

  private final static String exit = """
      mov rax, 60
      xor rdi, rdi
      syscall
      """;

  public AssemblyBuilder() {
    this.finalAssembly = new StringBuilder(".intel_syntax noprefix\n.global _start\n");
    // CPU instructions
    this.text = new StringBuilder(".text\n_start:\n\n");
    // Read only data
    this.rodata = new StringBuilder("\n.section .rodata\n");
    // Global initialized variables, Read - Write
    this.data = new StringBuilder("\n.data\n");
    // Global uninitialized variables, Read - Write
    this.bss = new StringBuilder("\n.section .bss\n");
  }

  public String createAssembly(List<Instruction> IR) {
    for (Instruction inst : IR) {
      if (inst instanceof Alloc alloc) {
        handleAlloc(alloc);
      } else if (inst instanceof Assign assign) {
        handleAssign(assign);
      } else if (inst instanceof Call call) {
        handleCall(call);
      }
    }

    this.text.append(exit);

    this.finalAssembly.append(text);
    this.finalAssembly.append(rodata);
    this.finalAssembly.append(data);
    this.finalAssembly.append(bss);

    return this.finalAssembly.toString();
  }

  private void handleAssign(Assign assign) {
    String reassignLabel = assign.name() + "_reassign";
    this.stringVariables.put(assign.name(), new StringVar(assign.value(), true));

    this.rodata.append(String.format("""
        %s: .ascii "%s"
        """, reassignLabel, assign.value()));

    this.text.append(String.format("""
        lea rax, [rip + %s]
        mov [rip + %s], rax
        """, reassignLabel, assign.name()));
  }

  private void handleAlloc(Alloc alloc) {
    this.stringVariables.put(alloc.name(), new StringVar(alloc.initializer(), alloc.mutable()));
    if (alloc.mutable()) {
      handleAllocMut(alloc);
    } else {
      handleAllocRO(alloc);
    }
  }

  private void handleAllocMut(Alloc alloc) {
    if (alloc.type() == null) {
      this.bss.append(String.format("""
          %s: .skip 8
          """, alloc.name()));
    } else if (alloc.type().equals("string")) {
      String pointer = alloc.name() + "_ptr";
      this.rodata.append(String.format("""
          %s: .ascii "%s"
          """, pointer, alloc.initializer()));
      this.data.append(String.format("""
          %s: .quad %s
          """, alloc.name(), pointer));
    } else {
      throw new AssemblyBuilderException("Not a supported type");
    }
  }

  private void handleAllocRO(Alloc alloc) {
    if (alloc.type().equals("string")) {
      this.rodata.append(String.format("""
          %s: .ascii "%s"
          """, alloc.name(), alloc.initializer()));
    } else {
      throw new AssemblyBuilderException("Not a supported type to skriv");
    }
  }

  private void handleCall(Call call) {
    if (call.fn().equals("skriv")) {
      handlePrint(call);
    } else {
      throw new AssemblyBuilderException("unknown call type: " + call.fn());
    }
  }

  void handlePrint(Call call) {
    if (call.args().size() != 1) {
      throw new AssemblyBuilderException("Print only supports one argument");
    }
    String variableName = call.args().getFirst();
    StringVar var = this.stringVariables.get(variableName);
    int stringLength = var.value().length();

    if (var.mutable()) {
      this.text.append(String.format("""
        mov rsi, [rip + %s]
        mov rax, 1
        mov rdi, 1
        mov rdx, %d
        syscall
        
        """, variableName, stringLength));
    } else {
      this.text.append(String.format("""
        mov rax, 1
        mov rdi, 1
        lea rsi, %s
        mov rdx, %d
        syscall
        
        """, variableName, stringLength));
    }
  }
}