From 72a60be38681f340651b5bb0ff2ff166f11108dd Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 12 Jun 2022 23:48:50 +0200 Subject: [PATCH] Write an IR visitor --- Rakefile | 20 ++- source/elna/backend.d | 15 +- source/elna/elf.d | 329 ++++++++++++++++++++++++++++-------------- source/elna/ir.d | 67 ++++++++- source/elna/result.d | 8 + source/elna/riscv.d | 248 ++++++++++++++++++------------- 6 files changed, 465 insertions(+), 222 deletions(-) diff --git a/Rakefile b/Rakefile index bb4e232..908dfdf 100644 --- a/Rakefile +++ b/Rakefile @@ -49,20 +49,26 @@ task test: BINARY do .new(test) .sub_ext('.txt') .sub(/^build\/[[:alpha:]]+\//, 'tests/expectations/') - .read - .to_i + .to_path puts "Running #{test}" if test.include? '/riscv/' - system('/opt/riscv/bin/spike', - '/opt/riscv/riscv32-unknown-elf/bin/pk', test, - { out: '/dev/null' }) + spike = [ + '/opt/riscv/bin/spike', + '/opt/riscv/riscv32-unknown-elf/bin/pk', + test + ] + diff = ['diff', '-Nur', '--color', expected, '-'] + tail = ['tail', '-n', '1'] + + last_stdout, wait_threads = Open3.pipeline_r spike, tail, diff else raise 'Unsupported test platform' end - actual = $?.exitstatus + print last_stdout.read + last_stdout.close - fail "#{test}: Expected #{expected}, got #{actual}" unless expected == actual + fail unless wait_threads.last.value.exitstatus.zero? end end diff --git a/source/elna/backend.d b/source/elna/backend.d index 33914de..e314a81 100644 --- a/source/elna/backend.d +++ b/source/elna/backend.d @@ -58,7 +58,7 @@ int generate(string inFile, ref String outputFilename) @nogc } auto program = writeNext(ir); auto elf = Elf!ELFCLASS32(move(handle)); - auto readOnlyData = Array!ubyte(cast(const(ubyte)[]) "%d".ptr[0 .. 3]); // With \0. + auto readOnlyData = Array!ubyte(cast(const(ubyte)[]) "%d\n".ptr[0 .. 4]); // With \0. Array!Relocation relocationData; foreach (ref reference; program.symbols) @@ -66,7 +66,18 @@ int generate(string inFile, ref String outputFilename) @nogc Relocation relocationEntry; relocationEntry.symbol = reference; - relocationEntry.typeInformation = R_RISCV_CALL; + final switch (reference.target) + { + case Reference.Target.text: + relocationEntry.typeInformation = R_RISCV_CALL; + break; + case Reference.Target.high20: + relocationEntry.typeInformation = R_RISCV_HI20; + break; + case Reference.Target.lower12i: + relocationEntry.typeInformation = R_RISCV_LO12_I; + break; + } relocationEntry.hasEntry = true; relocationData.insertBack(relocationEntry); diff --git a/source/elna/elf.d b/source/elna/elf.d index 9aa2806..592db13 100644 --- a/source/elna/elf.d +++ b/source/elna/elf.d @@ -655,19 +655,27 @@ struct Elf(ubyte elfClass) static assert(false, "Invalid ELF class"); } - private Elf32_Ehdr fileHeader; - private Array!Elf32_Shdr sectionHeaders; - private Elf32_Off currentOffset = Elf32_Ehdr.sizeof; - private Array!Elf32_Sym symbols; + private Array!Shdr sectionHeaders; + private Off currentOffset = Elf32_Ehdr.sizeof; + private Array!Sym symbols; static immutable char[52] sections = "\0.symtab\0.strtab\0.shstrtab\0.text\0.rodata\0.rela.text\0"; private String strings; private Word lastLocalSymbol; - private Word textSize; private File output; private Array!ubyte readOnly; private Array!Rela relocations; + private enum HeaderName + { + text = 0x1b, + roData = 0x21, + string_ = 0x09, + headerString = 0x11, + symbol = 0x01, + rela = 0x29 + } + static Elf opCall(File output) @nogc { Elf elf = Elf.init; @@ -676,7 +684,14 @@ struct Elf(ubyte elfClass) elf.insertSymbols(); elf.output = move(output); - elf.output.seek(Elf32_Ehdr.sizeof, File.Whence.set); + elf.output.seek(Ehdr.sizeof, File.Whence.set); + + elf.makeTextHeader(); + elf.makeRoDataHeader(); + elf.makeSymbolHeader(); + elf.makeRelaHeader(); + elf.makeStringHeader!(HeaderName.string_)(); + elf.makeStringHeader!(HeaderName.headerString)(); return elf; } @@ -685,34 +700,16 @@ struct Elf(ubyte elfClass) void finish() @nogc { - makeTextHeader(); - makeRelaHeader(cast(Word) (this.sectionHeaders.length + 2), cast(Word) (this.sectionHeaders.length - 1)); - makeRoDataHeader(); - initializeSymbolTable(cast(Word) (this.sectionHeaders.length + 1)); - - foreach (symbol; this.symbols) - { - output.write((cast(ubyte*) &symbol)[0 .. Elf32_Sym.sizeof]); - this.currentOffset += Elf32_Sym.sizeof; - } - - this.sectionHeaders.insertBack(makeStringHeader(0x09, this.currentOffset, cast(Elf32_Word) strings.length)); - output.write(cast(ubyte[]) this.strings.toStringz[0 .. this.strings.length + 1]); - this.currentOffset += this.strings.length + 1; - - this.sectionHeaders.insertBack(makeStringHeader(0x11, this.currentOffset, sections.length)); - output.write(cast(const(ubyte)[]) this.sections); - this.currentOffset += this.sections.length; - auto alignment = pad!ELFCLASS32(this.strings.length + 1 + this.sections.length); - const(ubyte)[4] padding = 0; - output.write(padding[0 .. alignment - this.strings.length - 1 - this.sections.length]); - this.currentOffset += alignment - this.strings.length - 1 - this.sections.length; + writeRelaTable(); + writeRoDataTable(); + writeSymbolTable(); + writeStringTables(); // End writing data, start writing headers. - output.write((cast(ubyte*) this.sectionHeaders.get)[0 .. Elf32_Shdr.sizeof * this.sectionHeaders.length]); + output.write((cast(ubyte*) this.sectionHeaders.get)[0 .. Shdr.sizeof * this.sectionHeaders.length]); - this.initializeFileHeader(); + writeFileHeader(); } private void insertSymbols() @nogc @@ -730,62 +727,113 @@ struct Elf(ubyte elfClass) this.lastLocalSymbol = cast(Elf32_Word) this.symbols.length; } - private Elf32_Shdr makeStringHeader(Elf32_Word stringIndex, Elf32_Off offset, Elf32_Word size) @nogc + private void makeStringHeader(HeaderName position)() @nogc { - Elf32_Shdr table; + Shdr table; - table.sh_name = stringIndex; + table.sh_name = position; table.sh_type = SHT_STRTAB; table.sh_flags = 0; table.sh_addr = 0; - table.sh_offset = offset; - table.sh_size = size; + table.sh_offset = 0; + table.sh_size = 0; table.sh_link = SHN_UNDEF; table.sh_info = 0; table.sh_addralign = 1; table.sh_entsize = 0; - return table; + this.sectionHeaders.insertBack(table); } - private void initializeSymbolTable(Word stringTableIndex) @nogc + private void writeStringTables() @nogc { - Elf32_Shdr symbolTableHeader; + auto stringIndex = findHeader!(HeaderName.string_); + assert(stringIndex != -1); - symbolTableHeader.sh_name = 0x01; + this.sectionHeaders[stringIndex].sh_offset = this.currentOffset; + this.sectionHeaders[stringIndex].sh_size = cast(Word) strings.length; + + output.write(cast(ubyte[]) this.strings.toStringz[0 .. this.strings.length + 1]); + this.currentOffset += this.strings.length + 1; + + auto headerStringIndex = findHeader!(HeaderName.headerString); + assert(stringIndex != -1); + + this.sectionHeaders[headerStringIndex].sh_offset = this.currentOffset; + this.sectionHeaders[headerStringIndex].sh_size = cast(Word) sections.length; + + output.write(cast(const(ubyte)[]) this.sections); + this.currentOffset += this.sections.length; + auto alignment = pad!ELFCLASS32(this.strings.length + 1 + this.sections.length); + const(ubyte)[4] padding = 0; + output.write(padding[0 .. alignment - this.strings.length - 1 - this.sections.length]); + this.currentOffset += alignment - this.strings.length - 1 - this.sections.length; + } + + private void makeSymbolHeader() @nogc + { + Shdr symbolTableHeader; + + symbolTableHeader.sh_name = HeaderName.symbol; symbolTableHeader.sh_type = SHT_SYMTAB; symbolTableHeader.sh_flags = 0; symbolTableHeader.sh_addr = 0; - symbolTableHeader.sh_offset = this.currentOffset; - symbolTableHeader.sh_size = cast(Elf32_Word) (this.symbols.length * Elf32_Sym.sizeof); + symbolTableHeader.sh_offset = 0; + symbolTableHeader.sh_size = 0; // String table used by entries in this section. - symbolTableHeader.sh_link = stringTableIndex; - symbolTableHeader.sh_info = this.lastLocalSymbol; + symbolTableHeader.sh_link = 0; + symbolTableHeader.sh_info = 0; symbolTableHeader.sh_addralign = 4; - symbolTableHeader.sh_entsize = Elf32_Sym.sizeof; + symbolTableHeader.sh_entsize = Sym.sizeof; this.sectionHeaders.insertBack(symbolTableHeader); } + private void writeSymbolTable() @nogc + { + auto index = findHeader!(HeaderName.symbol)(); + auto stringIndex = findHeader!(HeaderName.string_)(); + + assert(index != -1); + assert(stringIndex != -1); + + this.sectionHeaders[index].sh_offset = this.currentOffset; + this.sectionHeaders[index].sh_info = this.lastLocalSymbol; + this.sectionHeaders[index].sh_link = cast(Word) stringIndex; + this.sectionHeaders[index].sh_size = cast(Word) (this.symbols.length * Sym.sizeof); + + foreach (symbol; this.symbols) + { + output.write((cast(ubyte*) &symbol)[0 .. Sym.sizeof]); + this.currentOffset += Sym.sizeof; + } + } + void addCode(ref String name, ref Array!ubyte text, Array!Relocation usedSymbols) @nogc { this.output.write(text.get); + auto textHeaderIndex = findHeader!(HeaderName.text)(); + assert(textHeaderIndex != -1); + this.strings.insertBack("\0"); - this.strings.insertBack(name[]); Sym symbol; // Main function - symbol.st_name = 0x1; // Word - symbol.st_value = 0; // Addr - symbol.st_size = cast(Elf32_Word) text.length; // Word - symbol.st_info = ST_INFO(STB_GLOBAL, STT_FUNC); // char + symbol.st_name = cast(Word) this.strings.length; + symbol.st_value = 0; + symbol.st_size = cast(Word) text.length; + symbol.st_info = ST_INFO(STB_GLOBAL, STT_FUNC); symbol.st_other = 0; // char // .text header index, half word - symbol.st_shndx = cast(Elf32_Half) this.sectionHeaders.length; + symbol.st_shndx = cast(Half) textHeaderIndex; this.symbols.insertBack(symbol); - this.textSize += text.length; + + this.strings.insertBack(name[]); + + this.sectionHeaders[textHeaderIndex].sh_size += text.length; + this.currentOffset += text.length; foreach (usedSymbol; usedSymbols) { @@ -794,58 +842,91 @@ struct Elf(ubyte elfClass) relocationEntry.r_offset = cast(Addr) usedSymbol.symbol.offset; if (usedSymbol.hasEntry) { - relocationEntry.r_info = ELF32_R_INFO(this.symbols.length, usedSymbol.typeInformation); - this.relocations.insertBack(relocationEntry); + if (usedSymbol.symbol.target == Reference.Target.text) + { + relocationEntry.r_info = R_INFO(this.symbols.length, usedSymbol.typeInformation); + this.relocations.insertBack(relocationEntry); + } + else + { + relocationEntry.r_info = R_INFO(1, usedSymbol.typeInformation); + this.relocations.insertBack(relocationEntry); + } } else { - relocationEntry.r_info = ELF32_R_INFO(0, usedSymbol.typeInformation); + relocationEntry.r_info = R_INFO(0, usedSymbol.typeInformation); this.relocations.insertBack(relocationEntry); continue; } - Sym usedSymbolEntry; + if (usedSymbol.symbol.target == Reference.Target.text) + { + Sym usedSymbolEntry; - this.strings.insertBack("\0"); - usedSymbolEntry.st_name = cast(Word) this.strings.length; - usedSymbolEntry.st_value = 0; - usedSymbolEntry.st_size = 0; - usedSymbolEntry.st_info = ST_INFO(STB_GLOBAL, STT_NOTYPE); - usedSymbolEntry.st_other = 0; - usedSymbolEntry.st_shndx = SHN_UNDEF; + this.strings.insertBack("\0"); + usedSymbolEntry.st_name = cast(Word) this.strings.length; + usedSymbolEntry.st_value = 0; + usedSymbolEntry.st_size = 0; + usedSymbolEntry.st_info = ST_INFO(STB_GLOBAL, STT_NOTYPE); + usedSymbolEntry.st_other = 0; + usedSymbolEntry.st_shndx = SHN_UNDEF; - this.strings.insertBack(usedSymbol.symbol.name[]); - this.strings.insertBack("\0"); - this.symbols.insertBack(usedSymbolEntry); + this.strings.insertBack(usedSymbol.symbol.name[]); + this.strings.insertBack("\0"); + this.symbols.insertBack(usedSymbolEntry); + } } } void addReadOnlyData(ref Array!ubyte data) @nogc { + auto roDataIndex = findHeader!(HeaderName.roData)(); + assert(roDataIndex != -1); + + this.strings.insertBack("\0"); + + Sym symbol; + // Main function + symbol.st_name = cast(Word) this.strings.length; + symbol.st_value = 0; + symbol.st_size = cast(Word) data.length; + symbol.st_info = ST_INFO(STB_LOCAL, STT_NOTYPE); + symbol.st_other = 0; // char + // .text header index, half word + symbol.st_shndx = cast(Half) roDataIndex; + this.symbols.insertBack(symbol); + ++this.lastLocalSymbol; + + this.strings.insertBack(".CL0"); this.readOnly.insertBack(data[]); } + private ptrdiff_t findHeader(HeaderName position)() + { + return countUntil!(header => header.sh_name == position)(this.sectionHeaders[]); + } + private void makeTextHeader() @nogc { - Elf32_Shdr textHeader; + Shdr textHeader; - textHeader.sh_name = 0x1b; + textHeader.sh_name = HeaderName.text; textHeader.sh_type = SHT_PROGBITS; textHeader.sh_flags = SHF_EXECINSTR | SHF_ALLOC; textHeader.sh_addr = 0; textHeader.sh_offset = this.currentOffset; - textHeader.sh_size = cast(Elf32_Word) this.textSize; + textHeader.sh_size = 0; textHeader.sh_link = SHN_UNDEF; textHeader.sh_info = 0; textHeader.sh_addralign = 1; textHeader.sh_entsize = 0; this.sectionHeaders.insertBack(textHeader); - this.currentOffset += this.textSize; } private void initializeSectionHeaders() @nogc { - Elf32_Shdr table; + Shdr table; table.sh_name = 0; table.sh_type = SHT_NULL; @@ -861,78 +942,110 @@ struct Elf(ubyte elfClass) this.sectionHeaders.insertBack(table); } - private void initializeFileHeader() @nogc + private void writeFileHeader() @nogc { + Ehdr fileHeader; + auto headerStringIndex = findHeader!(HeaderName.headerString)(); + + assert(headerStringIndex != -1); + // Magic number. - this.fileHeader.e_ident[0] = '\x7f'; - this.fileHeader.e_ident[1] = 'E'; - this.fileHeader.e_ident[2] = 'L'; - this.fileHeader.e_ident[3] = 'F'; + fileHeader.e_ident[0] = '\x7f'; + fileHeader.e_ident[1] = 'E'; + fileHeader.e_ident[2] = 'L'; + fileHeader.e_ident[3] = 'F'; - this.fileHeader.e_ident[4] = ELFCLASS32; - this.fileHeader.e_ident[5] = ELFDATA2LSB; - this.fileHeader.e_ident[6] = EV_CURRENT; - this.fileHeader.e_ident[7] = EI_OSABI.ELFOSABI_SYSV; - this.fileHeader.e_ident[8] = 0; + fileHeader.e_ident[4] = ELFCLASS32; + fileHeader.e_ident[5] = ELFDATA2LSB; + fileHeader.e_ident[6] = EV_CURRENT; + fileHeader.e_ident[7] = EI_OSABI.ELFOSABI_SYSV; + fileHeader.e_ident[8] = 0; - this.fileHeader.e_type = ET_REL; - this.fileHeader.e_machine = 0xf3; // EM_RISCV - this.fileHeader.e_version = EV_CURRENT; - this.fileHeader.e_entry = 0; - this.fileHeader.e_phoff = 0; - this.fileHeader.e_shoff = this.currentOffset; - this.fileHeader.e_flags = 0; - this.fileHeader.e_ehsize = Elf32_Ehdr.sizeof; - this.fileHeader.e_phentsize = 0; - this.fileHeader.e_phnum = 0; - this.fileHeader.e_shentsize = Elf32_Shdr.sizeof; - this.fileHeader.e_shnum = cast(Elf32_Half) this.sectionHeaders.length; + fileHeader.e_type = ET_REL; + fileHeader.e_machine = 0xf3; // EM_RISCV + fileHeader.e_version = EV_CURRENT; + fileHeader.e_entry = 0; + fileHeader.e_phoff = 0; + fileHeader.e_shoff = this.currentOffset; + fileHeader.e_flags = 0; + fileHeader.e_ehsize = Elf32_Ehdr.sizeof; + fileHeader.e_phentsize = 0; + fileHeader.e_phnum = 0; + fileHeader.e_shentsize = Elf32_Shdr.sizeof; + fileHeader.e_shnum = cast(Elf32_Half) this.sectionHeaders.length; // String table is the last one - this.fileHeader.e_shstrndx = cast(Elf32_Half) (this.sectionHeaders.length - 1); + fileHeader.e_shstrndx = cast(Half) headerStringIndex; output.seek(0, File.Whence.set); - output.write((cast(ubyte*) &this.fileHeader)[0 .. fileHeader.sizeof]); + output.write((cast(ubyte*) &fileHeader)[0 .. fileHeader.sizeof]); } private void makeRoDataHeader() @nogc { Shdr table; - table.sh_name = 0x21; + table.sh_name = HeaderName.roData; table.sh_type = SHT_PROGBITS; table.sh_flags = SHF_ALLOC; table.sh_addr = 0; - table.sh_offset = this.currentOffset; - table.sh_size = cast(Xword) this.readOnly.length; + table.sh_offset = 0; + table.sh_size = 0; table.sh_link = SHN_UNDEF; table.sh_info = 0; table.sh_addralign = 4; table.sh_entsize = 0; - output.write(this.readOnly.get); - this.sectionHeaders.insertBack(table); + } + + private void writeRoDataTable() @nogc + { + auto index = findHeader!(HeaderName.roData)(); + assert(index != -1); + + this.sectionHeaders[index].sh_offset = this.currentOffset; + this.sectionHeaders[index].sh_size = cast(Xword) this.readOnly.length; + + output.write(this.readOnly.get); this.currentOffset += this.readOnly.length; } - private void makeRelaHeader(Word symTableIndex, Word targetTableIndex) @nogc + private void makeRelaHeader() @nogc { Shdr table; - table.sh_name = 0x29; + table.sh_name = HeaderName.rela; table.sh_type = SHT_RELA; - table.sh_flags = this.relocations.length == 0 ? 0 : SHF_ALLOC; + table.sh_flags = 0; table.sh_addr = 0; - table.sh_offset = this.currentOffset; - table.sh_size = cast(Word) (Rela.sizeof * this.relocations.length); - table.sh_link = symTableIndex; - table.sh_info = targetTableIndex; + table.sh_offset = 0; + table.sh_size = 0; + table.sh_link = SHN_UNDEF; + table.sh_info = 0; table.sh_addralign = 4; table.sh_entsize = Rela.sizeof; - this.output.write((cast(ubyte*) this.relocations.get)[0 .. Rela.sizeof * this.relocations.length]); - this.currentOffset += Rela.sizeof * this.relocations.length; this.sectionHeaders.insertBack(table); } + + private void writeRelaTable() @nogc + { + auto index = findHeader!(HeaderName.rela); + auto textIndex = findHeader!(HeaderName.text)(); + auto symbolIndex = findHeader!(HeaderName.symbol)(); + + assert(index != -1); + assert(textIndex != -1); + assert(symbolIndex != -1); + + this.sectionHeaders[index].sh_link = cast(Word) symbolIndex; + this.sectionHeaders[index].sh_info = cast(Word) textIndex; + this.sectionHeaders[index].sh_flags = this.relocations.length == 0 ? 0 : SHF_ALLOC; + this.sectionHeaders[index].sh_offset = this.currentOffset; + this.sectionHeaders[index].sh_size = cast(Word) (Rela.sizeof * this.relocations.length); + + this.output.write((cast(ubyte*) this.relocations.get)[0 .. Rela.sizeof * this.relocations.length]); + this.currentOffset += Rela.sizeof * this.relocations.length; + } } diff --git a/source/elna/ir.d b/source/elna/ir.d index e2a8df4..4fe8858 100644 --- a/source/elna/ir.d +++ b/source/elna/ir.d @@ -7,43 +7,100 @@ import tanya.container.string; import tanya.memory.allocator; import tanya.memory.mmappool; +/** + * IR visitor. +*/ +abstract class IRVisitor +{ + abstract void visit(Node) @nogc; + abstract void visit(Definition) @nogc; + abstract void visit(Expression) @nogc; + abstract void visit(Statement) @nogc; + abstract void visit(Variable) @nogc; + abstract void visit(VariableDeclaration) @nogc; + abstract void visit(Number) @nogc; + abstract void visit(Subroutine) @nogc; +} + +/** + * AST node. + */ +abstract class Node +{ + abstract void accept(IRVisitor) @nogc; +} + /** * Definition. */ -class Definition +class Definition : Node { char[] identifier; Array!Statement statements; Array!VariableDeclaration variableDeclarations; + + override void accept(IRVisitor visitor) @nogc + { + visitor.visit(this); + } } -class Statement +class Statement : Node { Subroutine subroutine; + + override void accept(IRVisitor visitor) @nogc + { + visitor.visit(this); + } } -abstract class Expression +abstract class Expression : Node { + override void accept(IRVisitor visitor) @nogc + { + visitor.visit(this); + } } class Number : Expression { int value; + + override void accept(IRVisitor visitor) @nogc + { + visitor.visit(this); + } } class Variable : Expression { size_t counter; + + override void accept(IRVisitor visitor) @nogc + { + visitor.visit(this); + } } -class VariableDeclaration +class VariableDeclaration : Node { String identifier; + + override void accept(IRVisitor visitor) @nogc + { + visitor.visit(this); + } } -class Subroutine +class Subroutine : Node { Expression lhs, rhs; + + override void accept(IRVisitor visitor) @nogc + { + visitor.visit(this); + } } private Number transformNumber(parser.Number number) @nogc diff --git a/source/elna/result.d b/source/elna/result.d index 9ed62d1..c99ab50 100644 --- a/source/elna/result.d +++ b/source/elna/result.d @@ -87,8 +87,16 @@ struct Result(T) struct Reference { + enum Target + { + text, + high20, + lower12i + } + String name; size_t offset; + Target target; } struct Symbol diff --git a/source/elna/riscv.d b/source/elna/riscv.d index 91506a0..0319b0c 100644 --- a/source/elna/riscv.d +++ b/source/elna/riscv.d @@ -7,6 +7,7 @@ import std.algorithm; import std.typecons; import tanya.container.array; import tanya.container.string; +import tanya.memory.allocator; enum XRegister : ubyte { @@ -171,115 +172,162 @@ struct Instruction } } +class RiscVVisitor : IRVisitor +{ + Array!Instruction instructions; + bool registerInUse; + uint variableCounter = 1; + Array!Reference references; + + override void visit(Node) @nogc + { + } + + override void visit(Definition definition) @nogc + { + // Prologue. + this.instructions.insertBack( + Instruction(BaseOpcode.opImm) + .i(XRegister.sp, Funct3.addi, XRegister.sp, cast(uint) -32) + ); + this.instructions.insertBack( + Instruction(BaseOpcode.store) + .s(28, Funct3.sw, XRegister.sp, XRegister.s0) + ); + this.instructions.insertBack( + Instruction(BaseOpcode.store) + .s(24, Funct3.sw, XRegister.sp, XRegister.ra) + ); + this.instructions.insertBack( + Instruction(BaseOpcode.opImm) + .i(XRegister.s0, Funct3.addi, XRegister.sp, 32) + ); + + foreach (statement; definition.statements[]) + { + statement.accept(this); + } + foreach (variableDeclaration; definition.variableDeclarations[]) + { + variableDeclaration.accept(this); + } + // Print the result. + this.instructions.insertBack( + Instruction(BaseOpcode.opImm) + .i(XRegister.a1, Funct3.addi, XRegister.a0, 0) + ); + this.references.insertBack(Reference(String(".CL0"), instructions.length * 4, Reference.Target.high20)); + this.instructions.insertBack( + Instruction(BaseOpcode.lui).u(XRegister.a5, 0) + ); + this.references.insertBack(Reference(String(".CL0"), instructions.length * 4, Reference.Target.lower12i)); + this.instructions.insertBack( + Instruction(BaseOpcode.opImm).i(XRegister.a0, Funct3.addi, XRegister.a5, 0) + ); + this.references.insertBack(Reference(String("printf"), instructions.length * 4, Reference.Target.text)); + this.instructions.insertBack( + Instruction(BaseOpcode.auipc).u(XRegister.ra, 0) + ); + this.instructions.insertBack( + Instruction(BaseOpcode.jalr) + .i(XRegister.ra, Funct3.jalr, XRegister.ra, 0) + ); + // Set the return value (0). + this.instructions.insertBack( + Instruction(BaseOpcode.op) + .r(XRegister.a0, Funct3.and, XRegister.zero, XRegister.zero) + ); + + // Epilogue. + this.instructions.insertBack( + Instruction(BaseOpcode.load) + .i(XRegister.s0, Funct3.lw, XRegister.sp, 28) + ); + this.instructions.insertBack( + Instruction(BaseOpcode.load) + .i(XRegister.ra, Funct3.lw, XRegister.sp, 24) + ); + this.instructions.insertBack( + Instruction(BaseOpcode.opImm) + .i(XRegister.sp, Funct3.addi, XRegister.sp, 32) + ); + this.instructions.insertBack( + Instruction(BaseOpcode.jalr) + .i(XRegister.zero, Funct3.jalr, XRegister.ra, 0) + ); + } + + override void visit(Expression) @nogc + { + } + + override void visit(Statement statement) @nogc + { + statement.subroutine.accept(this); + } + + override void visit(Variable variable) @nogc + { + const freeRegister = this.registerInUse ? XRegister.a0 : XRegister.t0; + + // movl -x(%rbp), %eax; where x is a number. + this.instructions.insertBack( + Instruction(BaseOpcode.load) + .i(freeRegister, Funct3.lw, XRegister.sp, + cast(byte) (variable.counter * 4)) + ); + } + + override void visit(VariableDeclaration) @nogc + { + } + + override void visit(Number number) @nogc + { + const freeRegister = this.registerInUse ? XRegister.a0 : XRegister.t0; + + this.instructions.insertBack( + Instruction(BaseOpcode.opImm) // movl $x, %eax; where $x is a number. + .i(freeRegister, Funct3.addi, XRegister.zero, number.value) + ); + } + + override void visit(Subroutine subroutine) @nogc + { + this.registerInUse = true; + subroutine.lhs.accept(this); + this.registerInUse = false; + subroutine.rhs.accept(this); + + // Calculate the result and assign it to a variable on the stack. + this.instructions.insertBack( + Instruction(BaseOpcode.op) + .r(XRegister.a0, Funct3.add, XRegister.a0, XRegister.t0) + ); + this.instructions.insertBack( // movl %eax, -x(%rbp); where x is a number. + Instruction(BaseOpcode.store) + .s(cast(uint) (this.variableCounter * 4), Funct3.sw, XRegister.sp, XRegister.a0) + ); + + ++this.variableCounter; + } +} + Symbol writeNext(Definition ast) @nogc { Array!Instruction instructions; Array!Reference references; - - // Prologue. - instructions.insertBack( - Instruction(BaseOpcode.opImm) - .i(XRegister.sp, Funct3.addi, XRegister.sp, cast(uint) -32) - ); - instructions.insertBack( - Instruction(BaseOpcode.store) - .s(28, Funct3.sw, XRegister.sp, XRegister.s0) - ); - instructions.insertBack( - Instruction(BaseOpcode.store) - .s(24, Funct3.sw, XRegister.sp, XRegister.ra) - ); - instructions.insertBack( - Instruction(BaseOpcode.opImm) - .i(XRegister.s0, Funct3.addi, XRegister.sp, 32) - ); - - // Code generation. - references.insertBack(Reference(String("putchar"), instructions.length * 4)); - instructions.insertBack( - Instruction(BaseOpcode.auipc).u(XRegister.ra, 0) - ); - instructions.insertBack( - Instruction(BaseOpcode.jalr) - .i(XRegister.ra, Funct3.jalr, XRegister.ra, 0) - ); - - int i = 1; - foreach (statement; ast.statements[]) + auto visitor = defaultAllocator.make!RiscVVisitor; + scope (exit) { - if ((cast(Number) statement.subroutine.lhs) !is null) - { - // Opcode of mov is “0xb8 + r”, where “r” is the register opcode. - // Register opcode of %eax is 0. - instructions.insertBack( - Instruction(BaseOpcode.opImm) // movl $x, %eax; where $x is a number. - .i(XRegister.a0, Funct3.addi, XRegister.zero, - (cast(Number) statement.subroutine.lhs).value) - ); - } - else if ((cast(Variable) statement.subroutine.lhs) !is null) - { - // movl -x(%rbp), %eax; where x is a number. - instructions.insertBack( - Instruction(BaseOpcode.load) - .i(XRegister.a0, Funct3.lw, XRegister.sp, - cast(byte) (cast(Variable) statement.subroutine.lhs).counter * 4) - ); - } - if ((cast(Number) statement.subroutine.rhs) !is null) - { - // Opcode of mov is “0xb8 + r”, where “r” is the register opcode. - // Register opcode of %ebx is 3. - instructions.insertBack( - Instruction(BaseOpcode.opImm) // movl $x, %ebx; where $x is a number. - .i(XRegister.t0, Funct3.addi, XRegister.zero, - (cast(Number) statement.subroutine.rhs).value) - ); - } - else if ((cast(Variable) statement.subroutine.rhs) !is null) - { - // movl -x(%rbp), %ebx; where x is a number. - instructions.insertBack( - Instruction(BaseOpcode.load) - .i(XRegister.t0, Funct3.lw, XRegister.sp, - cast(byte) (cast(Variable) statement.subroutine.rhs).counter * 4) - ); - } - // Calculate the result and assign it to a variable on the stack. - instructions.insertBack( - Instruction(BaseOpcode.op) - .r(XRegister.a0, Funct3.add, XRegister.a0, XRegister.t0) - ); - - instructions.insertBack( // movl %eax, -x(%rbp); where x is a number. - Instruction(BaseOpcode.store) - .s(cast(uint) (i * 4), Funct3.sw, XRegister.sp, XRegister.a0) - ); - ++i; + defaultAllocator.dispose(visitor); } - - // Prologue. - instructions.insertBack( - Instruction(BaseOpcode.load) - .i(XRegister.s0, Funct3.lw, XRegister.sp, 28) - ); - instructions.insertBack( - Instruction(BaseOpcode.load) - .i(XRegister.ra, Funct3.lw, XRegister.sp, 24) - ); - instructions.insertBack( - Instruction(BaseOpcode.opImm) - .i(XRegister.sp, Funct3.addi, XRegister.sp, 32) - ); - instructions.insertBack( - Instruction(BaseOpcode.jalr) - .i(XRegister.zero, Funct3.jalr, XRegister.ra, 0) - ); + visitor.visit(ast); auto program = Symbol(String("main")); - program.symbols = move(references); - foreach (ref instruction; instructions) + program.symbols = move(visitor.references); + foreach (ref instruction; visitor.instructions) { program.text.insertBack(instruction.encode); }