-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Ensure instruction set does not need to depend on architectures.
Showing
7 changed files
with
258 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
#ifndef EXAMPLES_BRAINFUCK_X64_CODE_GENERATOR_H | ||
#define EXAMPLES_BRAINFUCK_X64_CODE_GENERATOR_H | ||
|
||
#include "jasmin/compile/x64/function_emitter.h" | ||
#include "jasmin/compile/x64/location_map.h" | ||
#include "nth/meta/type.h" | ||
|
||
namespace bf { | ||
|
||
struct X64CodeGenerator { | ||
void operator()(decltype(nth::type<Initialize>), | ||
jasmin::x64::FunctionEmitter &gen, | ||
jasmin::LocationMap const &) { | ||
gen.write({0x48, 0x81, 0xec, 0x30, 0x75, 0x00, 0x00}); // sub rsp, 0x7530 | ||
gen.mov(jasmin::x64::Register::rsi, jasmin::x64::Register::rsp); | ||
gen.write({ | ||
0xba, 0x01, 0x00, 0x00, 0x00, // mov edx, 0x1 | ||
0x48, 0x89, 0xe7, // mov rdi, rsp | ||
0x31, 0xc0, // xor eax eax | ||
0x48, 0xc7, 0xc1, 0x30, 0x75, 0x00, 0x00, // mov rcx, 0x1000 | ||
0xf3, 0xaa, // rep stos BYTE PTR es:[rdi],al | ||
}); | ||
} | ||
|
||
void operator()(decltype(nth::type<Increment>), | ||
jasmin::x64::FunctionEmitter &gen, | ||
jasmin::LocationMap const &) { | ||
gen.write({0x80, 0x06, 0x01}); // add BYTE PTR [rsi], 0x1 | ||
} | ||
|
||
void operator()(decltype(nth::type<Decrement>), | ||
jasmin::x64::FunctionEmitter &gen, | ||
jasmin::LocationMap const &) { | ||
gen.write({0x80, 0x2e, 0x01}); // sub BYTE PTR [rsi], 0x1 | ||
} | ||
|
||
void operator()(decltype(nth::type<Left>), jasmin::x64::FunctionEmitter &gen, | ||
jasmin::LocationMap const &) { | ||
gen.write({0x48, 0x8d, 0x76, 0xff}); // lea rsi, [rsi - 1] | ||
} | ||
|
||
void operator()(decltype(nth::type<Right>), jasmin::x64::FunctionEmitter &gen, | ||
jasmin::LocationMap const &) { | ||
gen.write({0x48, 0x8d, 0x76, 0x01}); // lea rsi, [rsi + 1] | ||
} | ||
|
||
void operator()(decltype(nth::type<Output>), | ||
jasmin::x64::FunctionEmitter &gen, | ||
jasmin::LocationMap const &) { | ||
gen.write({ | ||
0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, // mov rax, 0x1 | ||
0x48, 0xc7, 0xc7, 0x01, 0x00, 0x00, 0x00, // mov rdi, 0x1 | ||
0x48, 0xc7, 0xc2, 0x01, 0x00, 0x00, 0x00, // mov rdx, 0x1 | ||
}); | ||
gen.syscall(); | ||
} | ||
|
||
void operator()(decltype(nth::type<Input>), jasmin::x64::FunctionEmitter &gen, | ||
jasmin::LocationMap const &) { | ||
gen.write({ | ||
0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, // mov rax, 0x0 | ||
0x48, 0xc7, 0xc7, 0x00, 0x00, 0x00, 0x00, // mov rdi, 0x0 | ||
0x48, 0xc7, 0xc2, 0x01, 0x00, 0x00, 0x00, // mov rdx, 0x1 | ||
}); | ||
gen.syscall(); | ||
} | ||
|
||
void operator()(decltype(nth::type<Zero>), jasmin::x64::FunctionEmitter &gen, | ||
jasmin::LocationMap const &) { | ||
gen.write({0x8a, 0x06}); // mov al, BYTE PTR [rsi] | ||
} | ||
}; | ||
|
||
} // namespace bf | ||
|
||
#endif // EXAMPLES_BRAINFUCK_X64_CODE_GENERATOR_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#include "jasmin/compile/x64/function_emitter.h" | ||
|
||
namespace jasmin::x64 { | ||
|
||
void FunctionEmitter::write(std::initializer_list<uint8_t> instructions) { | ||
fn_->write(instructions); | ||
} | ||
|
||
void FunctionEmitter::push(Register reg) { | ||
write({static_cast<uint8_t>(static_cast<uint8_t>(reg) + 0x50)}); | ||
} | ||
|
||
void FunctionEmitter::pop(Register reg) { | ||
write({static_cast<uint8_t>(static_cast<uint8_t>(reg) + 0x58)}); | ||
} | ||
|
||
void FunctionEmitter::mov(Register destination, Register source) { | ||
write({0x48, 0x89, | ||
static_cast<uint8_t>(0xc0 + static_cast<uint8_t>(destination) + | ||
8 * static_cast<uint8_t>(source))}); | ||
} | ||
|
||
void FunctionEmitter::syscall() { write({0x0f, 0x05}); } | ||
|
||
void FunctionEmitter::ret() { write({0xc3}); } | ||
|
||
void FunctionEmitter::emit(SsaFunction const &fn, CompiledFunction &c) { | ||
fn_ = &c; | ||
push(Register::rbp); | ||
mov(Register::rbp, Register::rsp); | ||
|
||
block_starts_.reserve(fn.blocks().size()); | ||
LocationMap loc_map; | ||
for (auto const &block : fn.blocks()) { | ||
block_starts_.push_back(fn_->size()); | ||
for (auto const &inst : block.instructions()) { | ||
generators_[metadata_.opcode(inst.op_code())](generator_, *this, loc_map); | ||
} | ||
switch (block.branch().kind()) { | ||
case SsaBranchKind::Return: | ||
mov(Register::rsp, Register::rbp); | ||
pop(Register::rbp); | ||
ret(); | ||
break; | ||
case SsaBranchKind::Conditional: { | ||
auto const &c = block.branch().AsConditional(); | ||
// TODO: Support arbirtary register choices here. | ||
// TODO: Prefer fallthroughs when we can make that happen. | ||
write({ | ||
0x84, 0xc0, // test al, al | ||
0x0f, 0x84, 0x00, 0x00, 0x00, 0x00, // jz __ | ||
}); | ||
block_jumps_.emplace(fn_->size(), c.true_block); | ||
write({ | ||
0xe9, 0x00, 0x00, 0x00, 0x00, // jmp ___ | ||
}); | ||
block_jumps_.emplace(fn_->size(), c.false_block); | ||
} break; | ||
case SsaBranchKind::Unconditional: { | ||
NTH_UNIMPLEMENTED(); | ||
} break; | ||
case SsaBranchKind::Unreachable: break; | ||
} | ||
} | ||
|
||
for (auto const &[offset, block_number] : block_jumps_) { | ||
fn_->write_at(offset - 4, | ||
static_cast<uint32_t>(block_starts_[block_number] - offset)); | ||
} | ||
|
||
fn_ = nullptr; | ||
} | ||
|
||
} // namespace jasmin::x64 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
#ifndef JASMIN_COMPILE_X64_FUNCTION_EMITTER_H | ||
#define JASMIN_COMPILE_X64_FUNCTION_EMITTER_H | ||
|
||
#include <span> | ||
#include <string> | ||
#include <vector> | ||
|
||
#include "absl/container/flat_hash_map.h" | ||
#include "jasmin/compile/compiled_function.h" | ||
#include "jasmin/compile/x64/location_map.h" | ||
#include "jasmin/core/instruction.h" | ||
#include "jasmin/ssa/ssa.h" | ||
#include "nth/meta/type.h" | ||
|
||
namespace jasmin::x64 { | ||
|
||
enum class Register : uint8_t { | ||
rax = 0, | ||
rcx = 1, | ||
rdx = 2, | ||
rbx = 3, | ||
rsp = 4, | ||
rbp = 5, | ||
rsi = 6, | ||
rdi = 7, | ||
}; | ||
|
||
struct FunctionEmitter { | ||
FunctionEmitter(nth::Type auto instruction_set, auto &generator) | ||
: metadata_(Metadata<nth::type_t<instruction_set>>()), | ||
generator_(&generator_) { | ||
using generator_type = std::remove_reference_t<decltype(generator)>; | ||
nth::type_t<instruction_set>::instructions.reduce([this](auto... ts) { | ||
generators_ = {Generate<generator_type>(ts)...}; | ||
}); | ||
} | ||
|
||
void emit(SsaFunction const &fn, CompiledFunction &f); | ||
|
||
void write(std::initializer_list<uint8_t> instructions); | ||
|
||
void push(Register reg); | ||
void pop(Register reg); | ||
void mov(Register destination, Register source); | ||
void ret(); | ||
void syscall(); | ||
|
||
private: | ||
template <typename Generator> | ||
static auto Generate(nth::Type auto t) | ||
-> void (*)(void *, FunctionEmitter &, LocationMap const &); | ||
|
||
CompiledFunction *fn_ = nullptr; | ||
std::vector<size_t> block_starts_; | ||
absl::flat_hash_map<size_t, size_t> block_jumps_; | ||
InstructionSetMetadata const &metadata_; | ||
void *generator_; | ||
std::vector<void (*)(void *, FunctionEmitter &, LocationMap const &)> | ||
generators_; | ||
}; | ||
|
||
// Implementation | ||
|
||
template <typename Generator> | ||
auto FunctionEmitter::Generate(nth::Type auto t) | ||
-> void (*)(void *, FunctionEmitter &, LocationMap const &) { | ||
if constexpr (t == nth::type<Call>) { | ||
return nullptr; | ||
} else if constexpr (t == nth::type<Jump>) { | ||
return nullptr; | ||
} else if constexpr (t == nth::type<JumpIf>) { | ||
return nullptr; | ||
} else if constexpr (t == nth::type<JumpIfNot>) { | ||
return nullptr; | ||
} else if constexpr (t == nth::type<Return>) { | ||
return nullptr; | ||
} else { | ||
return +[](void *gen, FunctionEmitter &cg, LocationMap const &map) { | ||
(*reinterpret_cast<Generator *>(gen))(decltype(t){}, cg, map); | ||
}; | ||
} | ||
} | ||
|
||
} // namespace jasmin::x64 | ||
|
||
#endif // JASMIN_COMPILE_X64_FUNCTION_EMITTER_H |