Write an IR visitor
This commit is contained in:
parent
3f1492947c
commit
72a60be386
20
Rakefile
20
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
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -87,8 +87,16 @@ struct Result(T)
|
||||
|
||||
struct Reference
|
||||
{
|
||||
enum Target
|
||||
{
|
||||
text,
|
||||
high20,
|
||||
lower12i
|
||||
}
|
||||
|
||||
String name;
|
||||
size_t offset;
|
||||
Target target;
|
||||
}
|
||||
|
||||
struct Symbol
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user