|
// Generic machine code generator
|
|
// Copyright (C) Florian Negele
|
|
|
|
// This file is part of the Eigen Compiler Suite.
|
|
|
|
// The ECS is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// The ECS is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with the ECS. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
#include "asmassembler.hpp"
|
|
#include "asmgenerator.hpp"
|
|
#include "assembly.hpp"
|
|
#include "debugging.hpp"
|
|
#include "error.hpp"
|
|
#include "format.hpp"
|
|
#include "position.hpp"
|
|
#include "utilities.hpp"
|
|
|
|
#include <cstring>
|
|
|
|
#include "asmgeneratorcontext.hpp"
|
|
|
|
namespace ECS {
|
|
namespace Assembly {
|
|
|
|
Generator::Generator (Diagnostics& d, StringPool& sp, Assembler& a, const Target t,
|
|
const Name n, const Layout& l, const HasLinkRegister hlr) :
|
|
layout {l}, platform {layout, hlr}, assembler(a), target {t}, name {n}, parser {d, sp, false}
|
|
{
|
|
assert (target); assert (name);
|
|
}
|
|
|
|
void Generator::Generate (const Code::Sections& sections, const Source& source, Object::Binaries& binaries, Debugging::Information& information, std::ostream& listing) const
|
|
{
|
|
information.sources.push_back (source);
|
|
const auto bitmode = !std::strchr (name, ' ') && assembler.Validate (assembler.bitmode) ? assembler.bitmode : 0;
|
|
information.target.name = bitmode ? target + std::to_string (bitmode) : target;
|
|
information.target.endianness = assembler.endianness;
|
|
information.target.pointer = layout.pointer.size;
|
|
|
|
listing << "; " << name; if (bitmode) listing << ' ' << bitmode << "-bit"; listing << " assembly code listing generated from " << source << '\n';
|
|
Process (sections, binaries, information, listing);
|
|
}
|
|
|
|
Generator::Context::Context (const Generator& g, Object::Binaries& b, Debugging::Information& i,
|
|
std::ostream& l, const InitializeData id) :
|
|
listing(l), generator(g), endianness {generator.assembler.endianness}, binaries(b),
|
|
information(i) , initializeData {id}
|
|
{
|
|
}
|
|
|
|
void Generator::Context::EmitError (const Message& message) const
|
|
{
|
|
generator.assembler.diagnostics.Emit (Diagnostics::Error, section->name, currentInstruction + 1, message); throw Error {};
|
|
}
|
|
|
|
void Generator::Context::Process (const Code::Sections& sections)
|
|
{
|
|
if (initializeData) for (auto& section: sections) Initialize (section);
|
|
Batch (sections, [this] (const Code::Section& section) {Process (section);});
|
|
if (initializeData) for (auto& section: globalDefinitions) Initialize (section);
|
|
for (auto& section: globalDefinitions) Process (section);
|
|
}
|
|
|
|
void Generator::Context::Process (const Code::Section& section)
|
|
try
|
|
{
|
|
this->section = §ion;
|
|
if (IsAssembly (section)) return Batch (section.instructions, [this] (const Code::Instruction& instruction) {Assemble (instruction);});
|
|
|
|
if( !IsType (section.type) ) {
|
|
binaries.emplace_back (section);
|
|
binary = &binaries.back();
|
|
}else
|
|
binary = nullptr;
|
|
labels.resize (section.instructions.size () + 1); currentDirective = Lexer::Invalid; entry = nullptr; currentInstruction = 0;
|
|
|
|
if (listing && binary)
|
|
{
|
|
listing << '\n';
|
|
if (!section.comment.empty ()) listing << "; " << section.comment << '\n';
|
|
WriteIdentifier (listing << '.' << section.type << ' ', section.name) << '\n';
|
|
if (section.required) listing << '\t' << Assembly::Lexer::Required << '\n';
|
|
if (section.duplicable) listing << '\t' << Assembly::Lexer::Duplicable << '\n';
|
|
if (section.replaceable) listing << '\t' << Assembly::Lexer::Replaceable << '\n';
|
|
if (section.fixed) listing << '\t' << Lexer::Origin << '\t' << section.origin << '\n';
|
|
if (!section.group.empty ()) WriteIdentifier (listing << '\t' << Lexer::Group << '\t', section.group) << '\n';
|
|
}
|
|
|
|
if (IsCode (section.type)) binary->bytes.reserve (section.instructions.size () * generator.assembler.codeAlignment);
|
|
else if (IsData (section.type) && initializeData && HasDefinitions (section) && !section.required) Require ("_init_" + section.name);
|
|
else if (IsType (section.type)) AddTypeableEntry (Debugging::Entry::Type);
|
|
if (const auto original = binary) Preprocess (section), binary = original;
|
|
Batch (section.instructions, [this] (const Code::Instruction& instruction) {Emit (instruction);});
|
|
if (location) if (!IsType (*entry)) EmitError ("missing source code location"); else *location = {}, location = nullptr;
|
|
for (auto fixed: fixes) if (fixed) EmitError ("fixed register mapping");
|
|
if (!types.empty ()) EmitError ("missing type declaration");
|
|
|
|
for (auto symbol: symbols) assert (!symbol); symbols.clear (); declarations.clear ();
|
|
assert (std::count (uses, uses + Code::UserRegisters, 0) == Code::UserRegisters);
|
|
if (!pendingDefinitions.empty ()) ApplyLocalDefinitions (); appliedDefinitions.clear ();
|
|
|
|
if (listing && binary)
|
|
{
|
|
if (currentDirective) listing << '\n';
|
|
if (IsCode (section.type)) WriteIdentifier (listing << '\n' << Label {section.instructions.size ()} << ":\t; end of ", section.name) << '\n';
|
|
if (!binary->fixed && binary->alignment > 1) listing << '\t' << Lexer::Alignment << '\t' << binary->alignment << '\n';
|
|
}
|
|
|
|
if (const auto original = binary) Postprocess (section), binary = original;
|
|
|
|
if (entry && binary) entry->size = binary->bytes.size ();
|
|
if (binary) generator.assembler.Align (*binary), labels[section.instructions.size ()] = binary->bytes.size ();
|
|
for (auto& fixup: fixups) FixupInstruction ({binary->bytes.data () + fixup.offset, fixup.size}, binary->bytes.data () + labels[fixup.index], fixup.code); fixups.clear ();
|
|
}
|
|
catch (const Error&)
|
|
{
|
|
if (!pendingDefinitions.empty ()) ApplyLocalDefinitions (); appliedDefinitions.clear (); types.clear (); symbols.clear (); declarations.clear (); fixups.clear (); location = nullptr;
|
|
for (auto& fixed: fixes) fixed = 0; for (auto& used: uses) if (used) Release (Code::Register (&used - uses), used); throw;
|
|
}
|
|
|
|
void Generator::Context::Initialize (const Code::Section& section)
|
|
{
|
|
if (!IsData (section.type) || !HasDefinitions (section)) return;
|
|
|
|
Code::Section initDataSection {Code::Section::InitData, "_init_" + section.name, 0, section.required, section.duplicable, section.replaceable};
|
|
Format ("initialization of %0 section %1", section.type, section.name).swap (initDataSection.comment);
|
|
|
|
Code::Displacement displacement = 0; auto set = false;
|
|
for (auto instruction = section.instructions.begin (); instruction != section.instructions.end ();)
|
|
switch (instruction->mnemonic)
|
|
{
|
|
case Code::Instruction::ALIAS:
|
|
case Code::Instruction::REQ:
|
|
case Code::Instruction::LOC:
|
|
case Code::Instruction::FIELD:
|
|
case Code::Instruction::VALUE:
|
|
case Code::Instruction::VOID:
|
|
case Code::Instruction::TYPE:
|
|
case Code::Instruction::ARRAY:
|
|
case Code::Instruction::REC:
|
|
case Code::Instruction::PTR:
|
|
case Code::Instruction::REF:
|
|
case Code::Instruction::FUNC:
|
|
case Code::Instruction::ENUM:
|
|
++instruction;
|
|
break;
|
|
|
|
case Code::Instruction::DEF:
|
|
{
|
|
if (IsZero (instruction->operand1)) {displacement += instruction->operand1.type.size; ++instruction; break;}
|
|
if (!set) initDataSection.instructions.emplace_back (Code::Instruction::MOV, Code::Reg {generator.platform.pointer, Code::R0}, Code::Adr {generator.platform.pointer, section.name, displacement}), displacement = 0, set = true;
|
|
else if (displacement >= 128) initDataSection.instructions.emplace_back (Code::Instruction::ADD, Code::Reg {generator.platform.pointer, Code::R0}, Code::Reg {generator.platform.pointer, Code::R0}, Code::PtrImm (generator.platform.pointer, displacement)), displacement = 0;
|
|
const auto skip = std::find_if (instruction, section.instructions.end (), [instruction] (const Code::Instruction& other) {return *instruction != other;}) - instruction;
|
|
if (skip < 4) initDataSection.instructions.emplace_back (Code::Instruction::MOV, Code::Mem {instruction->operand1.type, Code::R0, displacement}, instruction->operand1), displacement += instruction->operand1.type.size, ++instruction;
|
|
else initDataSection.instructions.emplace_back (Code::Instruction::FILL, Code::Reg {generator.platform.pointer, Code::R0, displacement}, Code::PtrImm (generator.platform.pointer, Code::Pointer::Value (skip)), instruction->operand1), displacement += instruction->operand1.type.size * skip, instruction += skip;
|
|
break;
|
|
}
|
|
|
|
case Code::Instruction::RES:
|
|
displacement += instruction->operand1.size; ++instruction;
|
|
break;
|
|
|
|
default:
|
|
assert (Code::Instruction::Unreachable);
|
|
}
|
|
|
|
Process (initDataSection);
|
|
}
|
|
|
|
void Generator::Context::Check (const Code::Operand& operand) const
|
|
{
|
|
if (HasRegister (operand) && IsGeneral (operand.register_) && !uses[operand.register_]) EmitError ("unmapped register");
|
|
}
|
|
|
|
void Generator::Context::Acquire (const Code::Register register_, const Types& types, const Code::Size count)
|
|
{
|
|
assert (count); if (!uses[register_]) if (uses[register_] = count, !fixes[register_]) Acquire (register_, types);
|
|
}
|
|
|
|
void Generator::Context::AcquireRegister (const Code::Instruction& instruction)
|
|
{
|
|
Acquire (GetModifiedRegister (instruction), GetModifiedRegisterType (instruction), instruction);
|
|
Check (instruction.operand1); Check (instruction.operand2); Check (instruction.operand3);
|
|
}
|
|
|
|
void Generator::Context::Acquire (const Code::Register register_, const Code::Type& type, const Code::Instruction& instruction)
|
|
{
|
|
if (!IsUser (register_) || uses[register_]) return;
|
|
Code::Size uses = instruction.Uses (register_), fixes = 0; Types types {type};
|
|
if (uses > 1) Check (instruction.operand2), Check (instruction.operand3);
|
|
const auto last = §ion->instructions.front () + section->instructions.size ();
|
|
for (auto i = &instruction + 1; i != last; uses += i->Uses (register_), ++i)
|
|
{
|
|
if (i->mnemonic == Code::Instruction::FIX && i->operand1.Uses (register_)) ++fixes;
|
|
if (i->mnemonic == Code::Instruction::UNFIX && i->operand1.Uses (register_)) --fixes;
|
|
if (IsModifying (*i) && register_ == Code::RRes && !IsSupported (*i)) InsertUnique (i->operand1.type, types);
|
|
if (register_ == GetModifiedRegister (*i) && !i->operand2.Uses (register_) && !i->operand3.Uses (register_) && !fixes) break;
|
|
AddType (register_, i->operand1, types); AddType (register_, i->operand2, types); AddType (register_, i->operand3, types);
|
|
}
|
|
Acquire (register_, types, uses);
|
|
}
|
|
|
|
void Generator::Context::AddType (const Code::Register register_, const Code::Operand& operand, Types& types) const
|
|
{
|
|
if (HasRegister (operand) && operand.register_ == register_) InsertUnique (IsMemory (operand) ? generator.platform.pointer : operand.type, types);
|
|
}
|
|
|
|
void Generator::Context::ReleaseRegisters (const Code::Instruction& instruction)
|
|
{
|
|
for (auto register_ = Code::R0; register_ <= Code::RRes; register_ = Code::Register (register_ + 1)) Release (register_, instruction.Uses (register_));
|
|
}
|
|
|
|
void Generator::Context::Release (const Code::Register register_, const Code::Size count)
|
|
{
|
|
if (!IsUser (register_) || !count || !uses[register_]) return; assert (uses[register_] >= count);
|
|
if (!(uses[register_] -= count)) if (!fixes[register_]) Release (register_);
|
|
}
|
|
|
|
bool Generator::Context::IsLastUseOf (const Code::Register register_) const
|
|
{
|
|
return IsUser (register_) && uses[register_] == 1 && !fixes[register_];
|
|
}
|
|
|
|
void Generator::Context::Fix (const Code::Register register_)
|
|
{
|
|
if (IsUser (register_)) ++fixes[register_];
|
|
}
|
|
|
|
void Generator::Context::Unfix (const Code::Register register_)
|
|
{
|
|
if (IsUser (register_) && !--fixes[register_] && !uses[register_]) Release (register_);
|
|
}
|
|
|
|
void Generator::Context::List (const Code::Instruction& instruction)
|
|
{
|
|
if (instruction.mnemonic == Code::Instruction::DEF && instruction.comment.empty ()) return;
|
|
if (currentDirective) listing << '\n', currentDirective = Lexer::Invalid;
|
|
|
|
if (instruction.line || !instruction.comment.empty ()) listing << '\n';
|
|
const auto separateLabel = !instruction.line && (instruction.mnemonic == Code::Instruction::ALIAS || instruction.mnemonic == Code::Instruction::ASM);
|
|
if (separateLabel) listing << Label {currentInstruction} << ':';
|
|
if (!instruction.comment.empty ()) listing << "\t; " << instruction.comment << '\n'; else if (separateLabel) listing << '\n';
|
|
if (!instruction.line && IsCode (section->type) && !separateLabel) listing << Label {currentInstruction} << ':';
|
|
if (!instruction.line || !IsCode (section->type)) return;
|
|
listing << Label {currentInstruction} << ":\t; line " << instruction.line << ": ";
|
|
if (instruction.mnemonic == Code::Instruction::ASM) listing << instruction.mnemonic << '\n'; else listing << instruction << '\n';
|
|
}
|
|
|
|
void Generator::Context::Emit (const Code::Instruction& instruction)
|
|
try
|
|
{
|
|
assert (IsValid (instruction, *section, generator.platform));
|
|
currentInstruction = GetIndex (instruction, section->instructions);
|
|
if (binary) labels[currentInstruction] = binary->bytes.size ();
|
|
|
|
if (listing && binary) List (instruction);
|
|
const auto position = listing.tellp ();
|
|
Preprocess (instruction);
|
|
|
|
switch (instruction.mnemonic)
|
|
{
|
|
case Code::Instruction::ALIAS:
|
|
AddAlias (instruction.operand1.address);
|
|
AcquireRegister (instruction); ReleaseRegisters (instruction);
|
|
break;
|
|
|
|
case Code::Instruction::REQ:
|
|
Require (instruction.operand1.address);
|
|
break;
|
|
|
|
case Code::Instruction::DEF:
|
|
Define (instruction.operand1);
|
|
break;
|
|
|
|
case Code::Instruction::RES:
|
|
Reserve (instruction.operand1.size);
|
|
if (listing) listing << '\t' << Lexer::Reserve << '\t' << instruction.operand1.size << '\n';
|
|
break;
|
|
|
|
case Code::Instruction::NOP:
|
|
break;
|
|
|
|
case Code::Instruction::ASM:
|
|
AssembleInline (instruction.operand1.address, instruction.operand2.size, instruction.operand3.address);
|
|
break;
|
|
|
|
case Code::Instruction::FIX:
|
|
if (fixes[instruction.operand1.register_]) EmitError ("fixed register");
|
|
Acquire (instruction.operand1.register_, instruction.operand1.type, instruction);
|
|
Fix (instruction.operand1.register_); Release (instruction.operand1.register_, 1);
|
|
break;
|
|
|
|
case Code::Instruction::UNFIX:
|
|
if (!fixes[instruction.operand1.register_]) EmitError ("unfixed register");
|
|
Unfix (instruction.operand1.register_); Release (instruction.operand1.register_, 1);
|
|
break;
|
|
|
|
case Code::Instruction::LOC:
|
|
if (IsCode (section->type)) AddTypeableEntry (Debugging::Entry::Code);
|
|
else if (IsData (section->type)) AddTypeableEntry (Debugging::Entry::Data);
|
|
if (!location) EmitError ("invalid source code location");
|
|
location->index = Insert (instruction.operand1.address); location->line = instruction.operand2.size; location->column = instruction.operand3.size; location = nullptr;
|
|
break;
|
|
|
|
case Code::Instruction::BREAK:
|
|
AddTypeableEntry (Debugging::Entry::Code);
|
|
if (location) EmitError ("missing source code location");
|
|
entry->breakpoints.emplace_back (binary->bytes.size ());
|
|
location = &entry->breakpoints.back().location;
|
|
break;
|
|
|
|
case Code::Instruction::SYM:
|
|
if (IsRegister (instruction.operand3)) Acquire (instruction.operand3.register_, instruction.operand3.type, instruction);
|
|
Declare (instruction.operand1.offset, instruction.operand2.address, instruction.operand3);
|
|
if (HasRegister (instruction.operand3)) Release (instruction.operand3.register_, 1);
|
|
break;
|
|
|
|
case Code::Instruction::FIELD:
|
|
if (location) EmitError ("missing source code location");
|
|
if (declarations.empty () || !IsRecord (*declarations.back ().type)) EmitError ("invalid field declaration");
|
|
declarations.back ().type->fields.emplace_back (instruction.operand1.address, instruction.operand2.size, Convert (instruction.operand3));
|
|
location = &declarations.back ().type->fields.back().location;
|
|
types.push_back (&declarations.back ().type->fields.back ().type);
|
|
break;
|
|
|
|
case Code::Instruction::VALUE:
|
|
if (location) EmitError ("missing source code location");
|
|
if (declarations.empty () || !IsEnumeration (*declarations.back ().type)) EmitError ("invalid enumerator declaration");
|
|
declarations.back ().type->enumerators.emplace_back (instruction.operand1.address, GetValue (instruction.operand2));
|
|
location = &declarations.back ().type->enumerators.back().location;
|
|
break;
|
|
|
|
case Code::Instruction::VOID:
|
|
Declare (Debugging::Type::Void);
|
|
break;
|
|
|
|
case Code::Instruction::TYPE:
|
|
Declare (GetType (instruction.operand1));
|
|
break;
|
|
|
|
case Code::Instruction::ARRAY:
|
|
Declare ({Debugging::Type::Array, instruction.operand2.size}).index = instruction.operand1.size;
|
|
break;
|
|
|
|
case Code::Instruction::REC:
|
|
declarations.emplace_back (currentInstruction + instruction.operand1.offset, Declare ({Debugging::Type::Record, instruction.operand2.size}));
|
|
break;
|
|
|
|
case Code::Instruction::PTR:
|
|
Declare (Debugging::Type::Pointer);
|
|
break;
|
|
|
|
case Code::Instruction::REF:
|
|
Declare (Debugging::Type::Reference);
|
|
break;
|
|
|
|
case Code::Instruction::FUNC:
|
|
declarations.emplace_back (currentInstruction + instruction.operand1.offset, Declare (Debugging::Type::Function));
|
|
break;
|
|
|
|
case Code::Instruction::ENUM:
|
|
declarations.emplace_back (currentInstruction + instruction.operand1.offset, Declare (Debugging::Type::Enumeration));
|
|
break;
|
|
|
|
default:
|
|
Encode (instruction);
|
|
}
|
|
|
|
if (listing && IsCode (section->type) && binary && listing.tellp () == position && position != -1 && !instruction.line) listing << '\n';
|
|
for (auto& symbol: symbols) if (symbol && symbol->lifetime.end == currentInstruction) Undeclare (*symbol), symbol = nullptr;
|
|
for (auto& definition: pendingDefinitions) if (binary->bytes.size () > definition.limit) return ApplyLocalDefinitions ();
|
|
while (!declarations.empty () && declarations.back ().extent <= currentInstruction) declarations.pop_back ();
|
|
}
|
|
catch (const RegisterShortage&)
|
|
{
|
|
EmitError ("register shortage");
|
|
}
|
|
|
|
Code::Size Generator::Context::Push (const Code::Operand& operand)
|
|
{
|
|
assert (HasType (operand));
|
|
Check (operand); Generate (Code::PUSH {operand});
|
|
return generator.platform.GetStackSize (operand.type);
|
|
}
|
|
|
|
void Generator::Context::Encode (const Code::Instruction& instruction)
|
|
{
|
|
const auto modified = GetModifiedRegister (instruction);
|
|
const auto type = GetModifiedRegisterType (instruction);
|
|
if (IsUser (modified)) registerTypes[modified] = type;
|
|
|
|
if (IsSupported (instruction)) return AcquireRegister (instruction), Generate (instruction), ReleaseRegisters (instruction);
|
|
if (instruction.mnemonic == Code::Instruction::TRAP) return Generate (Code::CALL {Code::Adr {generator.platform.function, "abort"}, Code::Size {0}});
|
|
|
|
bool saved[Code::UserRegisters];
|
|
for (auto register_ = Code::R0; register_ <= Code::RRes; register_ = Code::Register (register_ + 1))
|
|
if (saved[register_] = register_ != modified && uses[register_] > instruction.Uses (register_)) Push (Code::Reg {registerTypes[register_], register_});
|
|
|
|
std::ostringstream address;
|
|
address << '_' << instruction.mnemonic;
|
|
if (HasType (instruction.operand1)) address << '_' << instruction.operand1.type;
|
|
if (HasType (instruction.operand2) && instruction.operand2.type != instruction.operand1.type) address << '_' << instruction.operand2.type;
|
|
if (HasType (instruction.operand3) && instruction.operand3.type != instruction.operand2.type) address << '_' << instruction.operand3.type;
|
|
|
|
Code::Size parameters = 0;
|
|
const auto modifying = IsModifying (instruction);
|
|
if (HasType (instruction.operand3)) parameters += Push (instruction.operand3);
|
|
if (HasType (instruction.operand2)) parameters += Push (DisplaceStackPointer (instruction.operand2, parameters));
|
|
if (HasType (instruction.operand1) && !modifying) parameters += Push (DisplaceStackPointer (instruction.operand1, parameters));
|
|
|
|
if (HasRegister (instruction.operand2)) Release (instruction.operand2.register_, 1);
|
|
if (HasRegister (instruction.operand3)) Release (instruction.operand3.register_, 1);
|
|
|
|
Generate (Code::CALL {Code::Adr {generator.platform.function, address.str ()}, parameters});
|
|
const Code::Reg result {modifying || modified != Code::RVoid ? type : Code::Unsigned {1}, Code::RRes};
|
|
if (modified != Code::RVoid) Acquire (modified, type, instruction);
|
|
|
|
const auto used = uses[result.register_];
|
|
if (used) Release (result.register_, used); Acquire (result.register_, {result.type});
|
|
if (modifying && modified != Code::RRes) Generate (Code::MOV {instruction.operand1, result});
|
|
if (IsOffset (instruction.operand1)) assert (!used); else Release (result.register_);
|
|
if (used) Acquire (result.register_, {registerTypes[result.register_]}, used);
|
|
|
|
for (auto register_ = Code::RRes; register_ >= Code::R0; register_ = Code::Register (register_ - 1))
|
|
if (saved[register_]) Generate (Code::POP {Code::Reg {registerTypes[register_], register_}});
|
|
|
|
if (IsOffset (instruction.operand1)) Generate (Code::BRNE {instruction.operand1.offset, result, Code::UImm (result.type, 0)}), Release (result.register_);
|
|
|
|
if (HasRegister (instruction.operand1)) Release (instruction.operand1.register_, 1);
|
|
}
|
|
|
|
void Generator::Context::AddFixup (const Label& label, const FixupCode code, const Code::Size size)
|
|
{
|
|
assert (label.index < labels.size ()); assert (size);
|
|
fixups.emplace_back (binary->bytes.size (), label.index, code, size); Reserve (size);
|
|
}
|
|
|
|
Code::Offset Generator::Context::GetOffset (const Byte*const byte) const
|
|
{
|
|
const Code::Size offset = byte - binary->bytes.data (); assert (offset < binary->bytes.size ()); return offset;
|
|
}
|
|
|
|
Code::Offset Generator::Context::GetBranchOffset (const Label& label, const Code::Offset defaultOffset) const
|
|
{
|
|
assert (label.index < labels.size ());
|
|
return label.index <= currentInstruction ? labels[label.index] - binary->bytes.size () : defaultOffset;
|
|
}
|
|
|
|
const Code::Instruction* Generator::Context::GetPreviousInstruction () const
|
|
{
|
|
return currentInstruction ? §ion->instructions[currentInstruction - 1] : nullptr;
|
|
}
|
|
|
|
Code::Operand Generator::Context::DisplaceStackPointer (const Code::Operand& operand, const Code::Displacement displacement)
|
|
{
|
|
auto result = operand; if (result.Uses (Code::RSP)) result.displacement += displacement; return result;
|
|
}
|
|
|
|
void Generator::Context::Require (const Object::Section::Name& name)
|
|
{
|
|
if (binary->AddRequirement (name) && listing) WriteIdentifier (listing << '\t' << Lexer::Require << '\t', name) << '\n';
|
|
}
|
|
|
|
void Generator::Context::SetGroup (const Object::Section::Name& name)
|
|
{
|
|
assert (binary->group.empty ()); binary->group = name;
|
|
if (listing) WriteIdentifier (listing << '\t' << Lexer::Group << '\t', name) << '\n';
|
|
}
|
|
|
|
void Generator::Context::AddAlias (const Code::Section::Name& name)
|
|
{
|
|
binary->aliases.emplace_back (name, binary->bytes.size ());
|
|
if (listing) WriteIdentifier (listing << '\t' << Lexer::Alias << '\t', name) << '\n';
|
|
}
|
|
|
|
void Generator::Context::AddLink (const Object::Section::Name& name, Object::Patch& patch)
|
|
{
|
|
patch.offset += binary->bytes.size (); binary->AddLink (name, patch);
|
|
}
|
|
|
|
void Generator::Context::AddSection (const Code::Section::Type type, const Code::Section::Name& name, const Code::Section::Required required, const Code::Section::Duplicable duplicable, const Code::Section::Replaceable replaceable)
|
|
{
|
|
binaries.emplace_back (type, name, 1, required, duplicable, replaceable);
|
|
binary = &binaries.back(); if (!listing) return;
|
|
WriteIdentifier (listing << '\n' << '.' << type << ' ', name) << '\n';
|
|
if (required) listing << '\t' << Assembly::Lexer::Required << '\n';
|
|
if (duplicable) listing << '\t' << Assembly::Lexer::Duplicable << '\n';
|
|
if (replaceable) listing << '\t' << Assembly::Lexer::Replaceable << '\n';
|
|
}
|
|
|
|
Generator::Context::LocalLabel Generator::Context::CreateLabel ()
|
|
{
|
|
const auto index = labels.size (); labels.push_back (0); return {*this, index};
|
|
}
|
|
|
|
Generator::Context::Label Generator::Context::GetLabel (const Code::Offset offset) const
|
|
{
|
|
const auto index = currentInstruction + offset + 1; assert (index <= section->instructions.size ()); return Label {index};
|
|
}
|
|
|
|
Code::Section::Name Generator::Context::DefineGlobal (const Code::Operand& value)
|
|
{
|
|
std::ostringstream stream; stream << '_' << value.type << '_' << std::hex << Convert (value);
|
|
const auto name = stream.str (); for (auto& section: globalDefinitions) if (section.name == name) return name;
|
|
globalDefinitions.emplace_back (Code::Section::Const, name, generator.platform.GetAlignment (value.type), false, true);
|
|
Format ("definition of %0", value).swap (globalDefinitions.back ().comment);
|
|
globalDefinitions.back ().instructions.emplace_back (Code::Instruction::DEF, value); return name;
|
|
}
|
|
|
|
Generator::Context::Label Generator::Context::DefineLocal (const Code::Operand& value, const Code::Size forward, const Code::Size backward)
|
|
{
|
|
const auto limit = binary->bytes.size () + forward;
|
|
for (auto& definition: pendingDefinitions) if (definition.value == value) return definition.limit = std::min (definition.limit, limit), definition.label;
|
|
for (auto& definition: appliedDefinitions) if (definition.value == value && binary->bytes.size () - labels[definition.label.index] <= backward) return definition.label;
|
|
pendingDefinitions.emplace_back (limit, *this, value);
|
|
return pendingDefinitions.back().label;
|
|
}
|
|
|
|
void Generator::Context::ApplyLocalDefinitions ()
|
|
{
|
|
assert (!pendingDefinitions.empty ()); if (listing) listing << "\n\t; local definitions\n";
|
|
if (!IsSink (section->instructions[currentInstruction].mnemonic)) Generate (Code::BR {Code::Offset (0)});
|
|
for (auto& definition: pendingDefinitions) Apply (definition); Align (generator.assembler.codeAlignment);
|
|
appliedDefinitions.splice (appliedDefinitions.end (), pendingDefinitions);
|
|
}
|
|
|
|
void Generator::Context::Apply (LocalDefinition& definition)
|
|
{
|
|
const auto alignment = generator.platform.GetAlignment (definition.value.type); AddAlignment (alignment);
|
|
Align (alignment); definition.label (); Define (definition.value); currentDirective = Lexer::Invalid; if (listing) listing << '\n';
|
|
}
|
|
|
|
void Generator::Context::Assemble (const Code::Instruction& instruction)
|
|
{
|
|
assert (IsValid (instruction, *section, generator.platform)); if (listing && listing.tellp ()) listing << '\n';
|
|
if (!instruction.comment.empty () && listing) listing << "; " << instruction.comment << '\n';
|
|
Assemble (instruction.operand1.address, instruction.operand2.size, instruction.operand3.address);
|
|
}
|
|
|
|
void Generator::Context::Assemble (const Source& source, const Line line, const Code::String& code)
|
|
{
|
|
Program program {source}; std::istringstream assembly {code};
|
|
generator.parser.Parse (assembly, line, program);
|
|
generator.assembler.Assemble (program, binaries);
|
|
if (listing) generator.printer.Print (program, listing);
|
|
}
|
|
|
|
void Generator::Context::AssembleInline (const Source& source, const Line line, const Code::String& code)
|
|
{
|
|
std::stringstream assembly;
|
|
for (auto symbol: symbols) if (symbol) Define (*symbol, assembly);
|
|
if (assembly.tellp ()) WriteLine (assembly, line) << '\n';
|
|
Instructions instructions; assembly << code;
|
|
generator.parser.Parse (assembly, source, line, instructions);
|
|
generator.assembler.Assemble (instructions, *binary);
|
|
if (listing) generator.printer.Print (instructions, listing);
|
|
}
|
|
|
|
void Generator::Context::Define (const Debugging::Symbol& symbol, std::ostream& assembly)
|
|
{
|
|
switch (symbol.model)
|
|
{
|
|
case Debugging::Symbol::Constant:
|
|
WriteValue (WriteDefinition (WriteIdentifier (assembly, symbol.name)), symbol.value) << '\n';
|
|
break;
|
|
|
|
case Debugging::Symbol::Register:
|
|
DefineRegister (GetDeclaration (symbol).operand3, symbol.name, assembly);
|
|
break;
|
|
|
|
case Debugging::Symbol::Variable:
|
|
WriteDefinition (WriteIdentifier (assembly, symbol.name)) << symbol.displacement << '\n';
|
|
break;
|
|
|
|
default:
|
|
assert (Debugging::Symbol::Unreachable);
|
|
}
|
|
}
|
|
|
|
void Generator::Context::DefineRegister (const Code::Operand& operand, const Debugging::Name& name, std::ostream& assembly)
|
|
{
|
|
assert (HasRegister (operand)); const auto parts = GetRegisterParts (operand);
|
|
for (Part part = 0; part != parts; ++part) DefineRegister (operand, name, part, assembly);
|
|
}
|
|
|
|
void Generator::Context::DefineRegister (const Code::Operand& operand, const Debugging::Name& name, const Part part, std::ostream& assembly)
|
|
{
|
|
if (const auto suffix = GetRegisterSuffix (operand, part)) WriteIdentifier (assembly, name + '.' + suffix); else WriteIdentifier (assembly, name);
|
|
WriteRegister (WriteDefinition (assembly), operand, part) << '\n';
|
|
}
|
|
|
|
void Generator::Context::Define (const Code::Operand& operand)
|
|
{
|
|
const auto size = operand.type.size;
|
|
assert (generator.platform.IsAligned (*section, operand.type));
|
|
assert (generator.platform.IsAligned (binary->bytes.size (), operand.type));
|
|
|
|
if (listing)
|
|
{
|
|
const auto directive = size == 8 ? Lexer::OByte : size == 4 ? Lexer::QByte : size == 3 ? Lexer::TByte : size == 2 ? Lexer::DByte : Lexer::Byte;
|
|
if (directive == currentDirective && !IsAddress (operand)) listing << ", ";
|
|
else if (currentDirective) listing << "\n\t" << directive << '\t'; else listing << '\t' << directive << '\t';
|
|
currentDirective = directive;
|
|
}
|
|
|
|
if (IsImmediate (operand)) Define (Convert (operand) >> GetPatchScale (operand.type), size);
|
|
else Define (GetAddress (operand), GetPatchMode (operand.type), operand.displacement, GetPatchScale (operand.type), size);
|
|
}
|
|
|
|
void Generator::Context::Define (const Object::Section::Name& name, const Object::Patch::Mode mode, const Object::Patch::Displacement displacement, const Object::Patch::Scale scale, const Code::Type::Size size)
|
|
{
|
|
Object::Patch patch {0, mode, displacement, scale}; patch.pattern = {size, endianness}; AddLink (name, patch); Reserve (size);
|
|
if (listing) WriteAddress (listing, GetFunction (mode), name, displacement, scale);
|
|
}
|
|
|
|
void Generator::Context::Define (const Code::Unsigned::Value value, const Code::Type::Size size)
|
|
{
|
|
Object::Pattern {size, endianness}.Patch (value, Reserve (size)); if (listing) listing << value;
|
|
}
|
|
|
|
Span<Byte> Generator::Context::Reserve (const Code::Size size)
|
|
{
|
|
const auto previous = binary->bytes.size (); binary->bytes.resize (previous + size); return {binary->bytes.data () + previous, size};
|
|
}
|
|
|
|
void Generator::Context::Align (const Code::Section::Alignment alignment)
|
|
{
|
|
if (listing && binary->bytes.size () % alignment) listing << '\t' << Lexer::Align << '\t' << alignment << '\n';
|
|
Reserve (ECS::Align (binary->bytes.size (), Code::Size (alignment)) - binary->bytes.size ());
|
|
}
|
|
|
|
void Generator::Context::AddAlignment (const Code::Section::Alignment alignment)
|
|
{
|
|
assert (IsPowerOfTwo (alignment)); if (alignment > generator.assembler.codeAlignment && !binary->fixed && alignment > binary->alignment) binary->alignment = alignment;
|
|
}
|
|
|
|
Debugging::Index Generator::Context::Insert (const Source& source)
|
|
{
|
|
for (auto& element: information.sources) if (element == source) return GetIndex (element, information.sources);
|
|
information.sources.emplace_back (source); return information.sources.size () - 1;
|
|
}
|
|
|
|
Debugging::Type& Generator::Context::Declare (Debugging::Type&& type)
|
|
{
|
|
if (IsCode (section->type)) AddTypeableEntry (Debugging::Entry::Code); else if (IsData (section->type)) AddTypeableEntry (Debugging::Entry::Data);
|
|
if (types.empty ())
|
|
if (!declarations.empty () && IsFunction (*declarations.back ().type)) {
|
|
declarations.back ().type->subtypes.emplace_back ();
|
|
types.push_back (&declarations.back ().type->subtypes.back());
|
|
}else EmitError ("invalid type declaration");
|
|
auto& result = *types.back ();
|
|
result = std::move (type); types.pop_back ();
|
|
for( auto i = result.subtypes.rbegin(); i != result.subtypes.rend(); ++i)
|
|
types.push_back(&(*i));
|
|
return result;
|
|
}
|
|
|
|
void Generator::Context::AddEntry (const Debugging::Entry::Model model)
|
|
{
|
|
information.entries.emplace_back (model, section->name);
|
|
if (!entry) entry = &information.entries.back(), entry->size = 0;
|
|
}
|
|
|
|
void Generator::Context::AddTypeableEntry (const Debugging::Entry::Model model)
|
|
{
|
|
if (!entry) AddEntry (model), location = &entry->location, types.push_back (&entry->type);
|
|
}
|
|
|
|
void Generator::Context::Declare (const Code::Offset offset, const Code::String& name, const Code::Operand& operand)
|
|
{
|
|
const Debugging::Lifetime lifetime = {offset < 0 ? currentInstruction + offset + 1 : currentInstruction, offset < 0 ? currentInstruction : currentInstruction + offset};
|
|
if (IsImmediate (operand)) Declare ({name, lifetime, GetValue (operand)});
|
|
else if (IsRegister (operand)) Declare ({name, lifetime, GetRegisterName (operand)});
|
|
else if (!operand.address.empty ()) Declare ({name, lifetime, operand.address, 0});
|
|
else Declare ({name, lifetime, GetRegisterName (operand), operand.displacement});
|
|
if (IsRegister (operand)) Fix (operand.register_);
|
|
}
|
|
|
|
void Generator::Context::Declare (Debugging::Symbol&& symbol)
|
|
{
|
|
if (IsCode (section->type)) AddTypeableEntry (Debugging::Entry::Code);
|
|
if (location) EmitError ("missing source code location"); entry->symbols.push_back (std::move (symbol));
|
|
location = &entry->symbols.back ().location; types.push_back (&entry->symbols.back ().type); symbols.push_back (&entry->symbols.back ());
|
|
}
|
|
|
|
void Generator::Context::Undeclare (Debugging::Symbol& symbol)
|
|
{
|
|
assert (symbol.lifetime.end == currentInstruction);
|
|
if (IsRegister (symbol)) Unfix (GetDeclaration (symbol).operand3.register_);
|
|
if (binary) symbol.lifetime = {labels[symbol.lifetime.begin], binary->bytes.size ()};
|
|
}
|
|
|
|
const Code::Instruction& Generator::Context::GetDeclaration (const Debugging::Symbol& symbol) const
|
|
{
|
|
assert (IsRegister (symbol));
|
|
return section->instructions[std::min (symbol.lifetime.begin, symbol.lifetime.end)];
|
|
}
|
|
|
|
Debugging::Name Generator::Context::GetRegisterName (const Code::Operand& operand)
|
|
{
|
|
assert (HasRegister (operand)); const auto parts = GetRegisterParts (operand); std::ostringstream stream;
|
|
for (Part part = 0; part != parts; ++part) WriteRegister (part ? stream << ':' : stream, operand, part);
|
|
return stream.str ();
|
|
}
|
|
|
|
Debugging::Type Generator::Context::GetType (const Code::Operand& operand)
|
|
{
|
|
if (IsString (operand)) return operand.address;
|
|
|
|
switch (operand.type.model)
|
|
{
|
|
case Code::Type::Signed:
|
|
return {Debugging::Type::Signed, operand.type.size};
|
|
case Code::Type::Unsigned:
|
|
return {Debugging::Type::Unsigned, operand.type.size};
|
|
case Code::Type::Float:
|
|
return {Debugging::Type::Float, operand.type.size};
|
|
case Code::Type::Pointer:
|
|
return {Debugging::Type::Pointer, 0, Debugging::Type::Void};
|
|
case Code::Type::Function:
|
|
return {Debugging::Type::Pointer, 0, Debugging::Type::Function};
|
|
default:
|
|
assert (Code::Type::Unreachable);
|
|
}
|
|
}
|
|
|
|
Debugging::Value Generator::Context::GetValue (const Code::Operand& operand)
|
|
{
|
|
assert (IsImmediate (operand));
|
|
|
|
switch (operand.type.model)
|
|
{
|
|
case Code::Type::Signed:
|
|
return operand.simm;
|
|
case Code::Type::Unsigned:
|
|
return operand.uimm;
|
|
case Code::Type::Float:
|
|
return operand.fimm;
|
|
case Code::Type::Pointer:
|
|
return operand.ptrimm;
|
|
case Code::Type::Function:
|
|
return operand.funimm;
|
|
default:
|
|
assert (Code::Type::Unreachable);
|
|
}
|
|
}
|
|
|
|
Generator::Context::Label::Label (const Code::Size i) :
|
|
index {i}
|
|
{
|
|
}
|
|
|
|
Generator::Context::LocalLabel::LocalLabel (Context& c, const Code::Size i) :
|
|
Label {i}, context {&c}
|
|
{
|
|
}
|
|
|
|
Generator::Context::LocalLabel::LocalLabel (LocalLabel&& label) noexcept :
|
|
Label {label.index}, context {label.context}
|
|
{
|
|
label.context = nullptr;
|
|
}
|
|
|
|
Generator::Context::LocalLabel::~LocalLabel ()
|
|
{
|
|
//if (!std::uncaught_exceptions ())
|
|
assert (!context);
|
|
}
|
|
|
|
void Generator::Context::LocalLabel::operator () ()
|
|
{
|
|
assert (context); assert (index < context->labels.size ());
|
|
assert (index > context->section->instructions.size ());
|
|
context->labels[index] = context->binary->bytes.size ();
|
|
if (context->listing) context->listing << *this << ':';
|
|
context = nullptr;
|
|
}
|
|
|
|
Generator::Context::LocalDefinition::LocalDefinition (const Code::Size l, Context& context, const Code::Operand& v) :
|
|
limit {l}, label {context.CreateLabel ()}, value {v}
|
|
{
|
|
}
|
|
|
|
Generator::Context::InstructionFixup::InstructionFixup (const Code::Size o, const Code::Size i, const FixupCode c, const Code::Size s) :
|
|
offset {o}, index {i}, code {c}, size {s}
|
|
{
|
|
}
|
|
|
|
Generator::Context::TypeDeclaration::TypeDeclaration (const Code::Size e, Debugging::Type& t) :
|
|
extent {e}, type {&t}
|
|
{
|
|
}
|
|
|
|
}} // ECS::Assembly
|