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) .new(test)
.sub_ext('.txt') .sub_ext('.txt')
.sub(/^build\/[[:alpha:]]+\//, 'tests/expectations/') .sub(/^build\/[[:alpha:]]+\//, 'tests/expectations/')
.read .to_path
.to_i
puts "Running #{test}" puts "Running #{test}"
if test.include? '/riscv/' if test.include? '/riscv/'
system('/opt/riscv/bin/spike', spike = [
'/opt/riscv/riscv32-unknown-elf/bin/pk', test, '/opt/riscv/bin/spike',
{ out: '/dev/null' }) '/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 else
raise 'Unsupported test platform' raise 'Unsupported test platform'
end 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
end end

View File

@ -58,7 +58,7 @@ int generate(string inFile, ref String outputFilename) @nogc
} }
auto program = writeNext(ir); auto program = writeNext(ir);
auto elf = Elf!ELFCLASS32(move(handle)); 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; Array!Relocation relocationData;
foreach (ref reference; program.symbols) foreach (ref reference; program.symbols)
@ -66,7 +66,18 @@ int generate(string inFile, ref String outputFilename) @nogc
Relocation relocationEntry; Relocation relocationEntry;
relocationEntry.symbol = reference; relocationEntry.symbol = reference;
final switch (reference.target)
{
case Reference.Target.text:
relocationEntry.typeInformation = R_RISCV_CALL; 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; relocationEntry.hasEntry = true;
relocationData.insertBack(relocationEntry); relocationData.insertBack(relocationEntry);

View File

@ -655,19 +655,27 @@ struct Elf(ubyte elfClass)
static assert(false, "Invalid ELF class"); static assert(false, "Invalid ELF class");
} }
private Elf32_Ehdr fileHeader; private Array!Shdr sectionHeaders;
private Array!Elf32_Shdr sectionHeaders; private Off currentOffset = Elf32_Ehdr.sizeof;
private Elf32_Off currentOffset = Elf32_Ehdr.sizeof; private Array!Sym symbols;
private Array!Elf32_Sym symbols;
static immutable char[52] sections = static immutable char[52] sections =
"\0.symtab\0.strtab\0.shstrtab\0.text\0.rodata\0.rela.text\0"; "\0.symtab\0.strtab\0.shstrtab\0.text\0.rodata\0.rela.text\0";
private String strings; private String strings;
private Word lastLocalSymbol; private Word lastLocalSymbol;
private Word textSize;
private File output; private File output;
private Array!ubyte readOnly; private Array!ubyte readOnly;
private Array!Rela relocations; 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 static Elf opCall(File output) @nogc
{ {
Elf elf = Elf.init; Elf elf = Elf.init;
@ -676,7 +684,14 @@ struct Elf(ubyte elfClass)
elf.insertSymbols(); elf.insertSymbols();
elf.output = move(output); 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; return elf;
} }
@ -685,34 +700,16 @@ struct Elf(ubyte elfClass)
void finish() @nogc void finish() @nogc
{ {
makeTextHeader(); writeRelaTable();
makeRelaHeader(cast(Word) (this.sectionHeaders.length + 2), cast(Word) (this.sectionHeaders.length - 1)); writeRoDataTable();
makeRoDataHeader(); writeSymbolTable();
initializeSymbolTable(cast(Word) (this.sectionHeaders.length + 1)); writeStringTables();
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;
// End writing data, start writing headers. // 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 private void insertSymbols() @nogc
@ -730,62 +727,113 @@ struct Elf(ubyte elfClass)
this.lastLocalSymbol = cast(Elf32_Word) this.symbols.length; 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_type = SHT_STRTAB;
table.sh_flags = 0; table.sh_flags = 0;
table.sh_addr = 0; table.sh_addr = 0;
table.sh_offset = offset; table.sh_offset = 0;
table.sh_size = size; table.sh_size = 0;
table.sh_link = SHN_UNDEF; table.sh_link = SHN_UNDEF;
table.sh_info = 0; table.sh_info = 0;
table.sh_addralign = 1; table.sh_addralign = 1;
table.sh_entsize = 0; 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_type = SHT_SYMTAB;
symbolTableHeader.sh_flags = 0; symbolTableHeader.sh_flags = 0;
symbolTableHeader.sh_addr = 0; symbolTableHeader.sh_addr = 0;
symbolTableHeader.sh_offset = this.currentOffset; symbolTableHeader.sh_offset = 0;
symbolTableHeader.sh_size = cast(Elf32_Word) (this.symbols.length * Elf32_Sym.sizeof); symbolTableHeader.sh_size = 0;
// String table used by entries in this section. // String table used by entries in this section.
symbolTableHeader.sh_link = stringTableIndex; symbolTableHeader.sh_link = 0;
symbolTableHeader.sh_info = this.lastLocalSymbol; symbolTableHeader.sh_info = 0;
symbolTableHeader.sh_addralign = 4; symbolTableHeader.sh_addralign = 4;
symbolTableHeader.sh_entsize = Elf32_Sym.sizeof; symbolTableHeader.sh_entsize = Sym.sizeof;
this.sectionHeaders.insertBack(symbolTableHeader); 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) void addCode(ref String name, ref Array!ubyte text, Array!Relocation usedSymbols)
@nogc @nogc
{ {
this.output.write(text.get); this.output.write(text.get);
auto textHeaderIndex = findHeader!(HeaderName.text)();
assert(textHeaderIndex != -1);
this.strings.insertBack("\0"); this.strings.insertBack("\0");
this.strings.insertBack(name[]);
Sym symbol; Sym symbol;
// Main function // Main function
symbol.st_name = 0x1; // Word symbol.st_name = cast(Word) this.strings.length;
symbol.st_value = 0; // Addr symbol.st_value = 0;
symbol.st_size = cast(Elf32_Word) text.length; // Word symbol.st_size = cast(Word) text.length;
symbol.st_info = ST_INFO(STB_GLOBAL, STT_FUNC); // char symbol.st_info = ST_INFO(STB_GLOBAL, STT_FUNC);
symbol.st_other = 0; // char symbol.st_other = 0; // char
// .text header index, half word // .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.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) foreach (usedSymbol; usedSymbols)
{ {
@ -794,15 +842,25 @@ struct Elf(ubyte elfClass)
relocationEntry.r_offset = cast(Addr) usedSymbol.symbol.offset; relocationEntry.r_offset = cast(Addr) usedSymbol.symbol.offset;
if (usedSymbol.hasEntry) if (usedSymbol.hasEntry)
{ {
relocationEntry.r_info = ELF32_R_INFO(this.symbols.length, usedSymbol.typeInformation); if (usedSymbol.symbol.target == Reference.Target.text)
{
relocationEntry.r_info = R_INFO(this.symbols.length, usedSymbol.typeInformation);
this.relocations.insertBack(relocationEntry); this.relocations.insertBack(relocationEntry);
} }
else else
{ {
relocationEntry.r_info = ELF32_R_INFO(0, usedSymbol.typeInformation); relocationEntry.r_info = R_INFO(1, usedSymbol.typeInformation);
this.relocations.insertBack(relocationEntry);
}
}
else
{
relocationEntry.r_info = R_INFO(0, usedSymbol.typeInformation);
this.relocations.insertBack(relocationEntry); this.relocations.insertBack(relocationEntry);
continue; continue;
} }
if (usedSymbol.symbol.target == Reference.Target.text)
{
Sym usedSymbolEntry; Sym usedSymbolEntry;
this.strings.insertBack("\0"); this.strings.insertBack("\0");
@ -818,34 +876,57 @@ struct Elf(ubyte elfClass)
this.symbols.insertBack(usedSymbolEntry); this.symbols.insertBack(usedSymbolEntry);
} }
} }
}
void addReadOnlyData(ref Array!ubyte data) @nogc 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[]); this.readOnly.insertBack(data[]);
} }
private ptrdiff_t findHeader(HeaderName position)()
{
return countUntil!(header => header.sh_name == position)(this.sectionHeaders[]);
}
private void makeTextHeader() @nogc 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_type = SHT_PROGBITS;
textHeader.sh_flags = SHF_EXECINSTR | SHF_ALLOC; textHeader.sh_flags = SHF_EXECINSTR | SHF_ALLOC;
textHeader.sh_addr = 0; textHeader.sh_addr = 0;
textHeader.sh_offset = this.currentOffset; textHeader.sh_offset = this.currentOffset;
textHeader.sh_size = cast(Elf32_Word) this.textSize; textHeader.sh_size = 0;
textHeader.sh_link = SHN_UNDEF; textHeader.sh_link = SHN_UNDEF;
textHeader.sh_info = 0; textHeader.sh_info = 0;
textHeader.sh_addralign = 1; textHeader.sh_addralign = 1;
textHeader.sh_entsize = 0; textHeader.sh_entsize = 0;
this.sectionHeaders.insertBack(textHeader); this.sectionHeaders.insertBack(textHeader);
this.currentOffset += this.textSize;
} }
private void initializeSectionHeaders() @nogc private void initializeSectionHeaders() @nogc
{ {
Elf32_Shdr table; Shdr table;
table.sh_name = 0; table.sh_name = 0;
table.sh_type = SHT_NULL; table.sh_type = SHT_NULL;
@ -861,78 +942,110 @@ struct Elf(ubyte elfClass)
this.sectionHeaders.insertBack(table); this.sectionHeaders.insertBack(table);
} }
private void initializeFileHeader() @nogc private void writeFileHeader() @nogc
{ {
Ehdr fileHeader;
auto headerStringIndex = findHeader!(HeaderName.headerString)();
assert(headerStringIndex != -1);
// Magic number. // Magic number.
this.fileHeader.e_ident[0] = '\x7f'; fileHeader.e_ident[0] = '\x7f';
this.fileHeader.e_ident[1] = 'E'; fileHeader.e_ident[1] = 'E';
this.fileHeader.e_ident[2] = 'L'; fileHeader.e_ident[2] = 'L';
this.fileHeader.e_ident[3] = 'F'; fileHeader.e_ident[3] = 'F';
this.fileHeader.e_ident[4] = ELFCLASS32; fileHeader.e_ident[4] = ELFCLASS32;
this.fileHeader.e_ident[5] = ELFDATA2LSB; fileHeader.e_ident[5] = ELFDATA2LSB;
this.fileHeader.e_ident[6] = EV_CURRENT; fileHeader.e_ident[6] = EV_CURRENT;
this.fileHeader.e_ident[7] = EI_OSABI.ELFOSABI_SYSV; fileHeader.e_ident[7] = EI_OSABI.ELFOSABI_SYSV;
this.fileHeader.e_ident[8] = 0; fileHeader.e_ident[8] = 0;
this.fileHeader.e_type = ET_REL; fileHeader.e_type = ET_REL;
this.fileHeader.e_machine = 0xf3; // EM_RISCV fileHeader.e_machine = 0xf3; // EM_RISCV
this.fileHeader.e_version = EV_CURRENT; fileHeader.e_version = EV_CURRENT;
this.fileHeader.e_entry = 0; fileHeader.e_entry = 0;
this.fileHeader.e_phoff = 0; fileHeader.e_phoff = 0;
this.fileHeader.e_shoff = this.currentOffset; fileHeader.e_shoff = this.currentOffset;
this.fileHeader.e_flags = 0; fileHeader.e_flags = 0;
this.fileHeader.e_ehsize = Elf32_Ehdr.sizeof; fileHeader.e_ehsize = Elf32_Ehdr.sizeof;
this.fileHeader.e_phentsize = 0; fileHeader.e_phentsize = 0;
this.fileHeader.e_phnum = 0; fileHeader.e_phnum = 0;
this.fileHeader.e_shentsize = Elf32_Shdr.sizeof; fileHeader.e_shentsize = Elf32_Shdr.sizeof;
this.fileHeader.e_shnum = cast(Elf32_Half) this.sectionHeaders.length; fileHeader.e_shnum = cast(Elf32_Half) this.sectionHeaders.length;
// String table is the last one // 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.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 private void makeRoDataHeader() @nogc
{ {
Shdr table; Shdr table;
table.sh_name = 0x21; table.sh_name = HeaderName.roData;
table.sh_type = SHT_PROGBITS; table.sh_type = SHT_PROGBITS;
table.sh_flags = SHF_ALLOC; table.sh_flags = SHF_ALLOC;
table.sh_addr = 0; table.sh_addr = 0;
table.sh_offset = this.currentOffset; table.sh_offset = 0;
table.sh_size = cast(Xword) this.readOnly.length; table.sh_size = 0;
table.sh_link = SHN_UNDEF; table.sh_link = SHN_UNDEF;
table.sh_info = 0; table.sh_info = 0;
table.sh_addralign = 4; table.sh_addralign = 4;
table.sh_entsize = 0; table.sh_entsize = 0;
output.write(this.readOnly.get);
this.sectionHeaders.insertBack(table); 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; this.currentOffset += this.readOnly.length;
} }
private void makeRelaHeader(Word symTableIndex, Word targetTableIndex) @nogc private void makeRelaHeader() @nogc
{ {
Shdr table; Shdr table;
table.sh_name = 0x29; table.sh_name = HeaderName.rela;
table.sh_type = SHT_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_addr = 0;
table.sh_offset = this.currentOffset; table.sh_offset = 0;
table.sh_size = cast(Word) (Rela.sizeof * this.relocations.length); table.sh_size = 0;
table.sh_link = symTableIndex; table.sh_link = SHN_UNDEF;
table.sh_info = targetTableIndex; table.sh_info = 0;
table.sh_addralign = 4; table.sh_addralign = 4;
table.sh_entsize = Rela.sizeof; 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); 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.allocator;
import tanya.memory.mmappool; 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. * Definition.
*/ */
class Definition class Definition : Node
{ {
char[] identifier; char[] identifier;
Array!Statement statements; Array!Statement statements;
Array!VariableDeclaration variableDeclarations; Array!VariableDeclaration variableDeclarations;
override void accept(IRVisitor visitor) @nogc
{
visitor.visit(this);
}
} }
class Statement class Statement : Node
{ {
Subroutine subroutine; 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 class Number : Expression
{ {
int value; int value;
override void accept(IRVisitor visitor) @nogc
{
visitor.visit(this);
}
} }
class Variable : Expression class Variable : Expression
{ {
size_t counter; size_t counter;
override void accept(IRVisitor visitor) @nogc
{
visitor.visit(this);
}
} }
class VariableDeclaration class VariableDeclaration : Node
{ {
String identifier; String identifier;
override void accept(IRVisitor visitor) @nogc
{
visitor.visit(this);
}
} }
class Subroutine class Subroutine : Node
{ {
Expression lhs, rhs; Expression lhs, rhs;
override void accept(IRVisitor visitor) @nogc
{
visitor.visit(this);
}
} }
private Number transformNumber(parser.Number number) @nogc private Number transformNumber(parser.Number number) @nogc

View File

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

View File

@ -7,6 +7,7 @@ import std.algorithm;
import std.typecons; import std.typecons;
import tanya.container.array; import tanya.container.array;
import tanya.container.string; import tanya.container.string;
import tanya.memory.allocator;
enum XRegister : ubyte enum XRegister : ubyte
{ {
@ -171,115 +172,162 @@ struct Instruction
} }
} }
Symbol writeNext(Definition ast) @nogc class RiscVVisitor : IRVisitor
{ {
Array!Instruction instructions; Array!Instruction instructions;
bool registerInUse;
uint variableCounter = 1;
Array!Reference references; Array!Reference references;
override void visit(Node) @nogc
{
}
override void visit(Definition definition) @nogc
{
// Prologue. // Prologue.
instructions.insertBack( this.instructions.insertBack(
Instruction(BaseOpcode.opImm) Instruction(BaseOpcode.opImm)
.i(XRegister.sp, Funct3.addi, XRegister.sp, cast(uint) -32) .i(XRegister.sp, Funct3.addi, XRegister.sp, cast(uint) -32)
); );
instructions.insertBack( this.instructions.insertBack(
Instruction(BaseOpcode.store) Instruction(BaseOpcode.store)
.s(28, Funct3.sw, XRegister.sp, XRegister.s0) .s(28, Funct3.sw, XRegister.sp, XRegister.s0)
); );
instructions.insertBack( this.instructions.insertBack(
Instruction(BaseOpcode.store) Instruction(BaseOpcode.store)
.s(24, Funct3.sw, XRegister.sp, XRegister.ra) .s(24, Funct3.sw, XRegister.sp, XRegister.ra)
); );
instructions.insertBack( this.instructions.insertBack(
Instruction(BaseOpcode.opImm) Instruction(BaseOpcode.opImm)
.i(XRegister.s0, Funct3.addi, XRegister.sp, 32) .i(XRegister.s0, Funct3.addi, XRegister.sp, 32)
); );
// Code generation. foreach (statement; definition.statements[])
references.insertBack(Reference(String("putchar"), instructions.length * 4)); {
instructions.insertBack( 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) Instruction(BaseOpcode.auipc).u(XRegister.ra, 0)
); );
instructions.insertBack( this.instructions.insertBack(
Instruction(BaseOpcode.jalr) Instruction(BaseOpcode.jalr)
.i(XRegister.ra, Funct3.jalr, XRegister.ra, 0) .i(XRegister.ra, Funct3.jalr, XRegister.ra, 0)
); );
// Set the return value (0).
int i = 1; this.instructions.insertBack(
foreach (statement; ast.statements[])
{
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) Instruction(BaseOpcode.op)
.r(XRegister.a0, Funct3.add, XRegister.a0, XRegister.t0) .r(XRegister.a0, Funct3.and, XRegister.zero, XRegister.zero)
); );
instructions.insertBack( // movl %eax, -x(%rbp); where x is a number. // Epilogue.
Instruction(BaseOpcode.store) this.instructions.insertBack(
.s(cast(uint) (i * 4), Funct3.sw, XRegister.sp, XRegister.a0)
);
++i;
}
// Prologue.
instructions.insertBack(
Instruction(BaseOpcode.load) Instruction(BaseOpcode.load)
.i(XRegister.s0, Funct3.lw, XRegister.sp, 28) .i(XRegister.s0, Funct3.lw, XRegister.sp, 28)
); );
instructions.insertBack( this.instructions.insertBack(
Instruction(BaseOpcode.load) Instruction(BaseOpcode.load)
.i(XRegister.ra, Funct3.lw, XRegister.sp, 24) .i(XRegister.ra, Funct3.lw, XRegister.sp, 24)
); );
instructions.insertBack( this.instructions.insertBack(
Instruction(BaseOpcode.opImm) Instruction(BaseOpcode.opImm)
.i(XRegister.sp, Funct3.addi, XRegister.sp, 32) .i(XRegister.sp, Funct3.addi, XRegister.sp, 32)
); );
instructions.insertBack( this.instructions.insertBack(
Instruction(BaseOpcode.jalr) Instruction(BaseOpcode.jalr)
.i(XRegister.zero, Funct3.jalr, XRegister.ra, 0) .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;
auto visitor = defaultAllocator.make!RiscVVisitor;
scope (exit)
{
defaultAllocator.dispose(visitor);
}
visitor.visit(ast);
auto program = Symbol(String("main")); auto program = Symbol(String("main"));
program.symbols = move(references); program.symbols = move(visitor.references);
foreach (ref instruction; instructions) foreach (ref instruction; visitor.instructions)
{ {
program.text.insertBack(instruction.encode); program.text.insertBack(instruction.encode);
} }