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)
|
.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
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user