Write an IR visitor
This commit is contained in:
		
							
								
								
									
										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);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user