Write an IR visitor

This commit is contained in:
Eugen Wissner 2022-06-12 23:48:50 +02:00
parent 3f1492947c
commit 72a60be386
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
6 changed files with 465 additions and 222 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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

View File

@ -87,8 +87,16 @@ struct Result(T)
struct Reference
{
enum Target
{
text,
high20,
lower12i
}
String name;
size_t offset;
Target target;
}
struct Symbol

View File

@ -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);
}