Skip to content

Commit

Permalink
Ensure instruction set does not need to depend on architectures.
Browse files Browse the repository at this point in the history
  • Loading branch information
asoffer committed Feb 2, 2024
1 parent 5c30ec3 commit b4c622a
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 70 deletions.
15 changes: 12 additions & 3 deletions examples/brainfuck/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ cc_library(
hdrs = ["instructions.h"],
deps = [
"//jasmin/core:instruction",
"//jasmin/compile/x64:code_generator",
"//jasmin/compile/x64:location_map",
],
)

Expand All @@ -50,9 +48,20 @@ cc_binary(
":build",
":file",
":instructions",
":x64_code_generator",
"//jasmin/core:function",
"//jasmin/ssa",
"//jasmin/compile/x64:code_generator",
"//jasmin/compile/x64:function_emitter",
"@nth_cc//nth/dynamic:jit_function",
],
)

cc_library(
name = "x64_code_generator",
hdrs = ["x64_code_generator.h"],
deps = [
"//jasmin/core:instruction",
"//jasmin/compile/x64:function_emitter",
"//jasmin/compile/x64:location_map",
],
)
60 changes: 0 additions & 60 deletions examples/brainfuck/instructions.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#ifndef EXAMPLES_BRAINFUCK_INSTRUCTIONS_H
#define EXAMPLES_BRAINFUCK_INSTRUCTIONS_H

#include "jasmin/compile/x64/code_generator.h"
#include "jasmin/compile/x64/location_map.h"
#include "jasmin/core/instruction.h"
#include "nth/debug/debug.h"

Expand All @@ -19,19 +17,6 @@ struct Initialize : jasmin::Instruction<Initialize> {
jasmin::Output<uint8_t *> out) {
out.set(state.buffer);
}

static void generate_code(jasmin::x64::CodeGenerator &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
});
}
};

struct Increment : jasmin::Instruction<Increment> {
Expand All @@ -40,11 +25,6 @@ struct Increment : jasmin::Instruction<Increment> {
jasmin::Output<>) {
++*in.get<0>();
}

static void generate_code(jasmin::x64::CodeGenerator &gen,
jasmin::LocationMap const &) {
gen.write({0x80, 0x06, 0x01}); // add BYTE PTR [rsi], 0x1
}
};

struct Decrement : jasmin::Instruction<Decrement> {
Expand All @@ -53,11 +33,6 @@ struct Decrement : jasmin::Instruction<Decrement> {
jasmin::Output<>) {
--*in.get<0>();
}

static void generate_code(jasmin::x64::CodeGenerator &gen,
jasmin::LocationMap const &) {
gen.write({0x80, 0x2e, 0x01}); // sub BYTE PTR [rsi], 0x1
}
};

struct Left : jasmin::Instruction<Left> {
Expand All @@ -68,11 +43,6 @@ struct Left : jasmin::Instruction<Left> {
NTH_REQUIRE(ptr != state.buffer);
out.set(ptr - 1);
}

static void generate_code(jasmin::x64::CodeGenerator &gen,
jasmin::LocationMap const &) {
gen.write({0x48, 0x8d, 0x76, 0xff}); // lea rsi, [rsi - 1]
}
};

struct Right : jasmin::Instruction<Right> {
Expand All @@ -83,11 +53,6 @@ struct Right : jasmin::Instruction<Right> {
NTH_REQUIRE(ptr < state.buffer + state.buffer_size);
out.set(ptr + 1);
}

static void generate_code(jasmin::x64::CodeGenerator &gen,
jasmin::LocationMap const &) {
gen.write({0x48, 0x8d, 0x76, 0x01}); // lea rsi, [rsi + 1]
}
};

struct Input : jasmin::Instruction<Input> {
Expand All @@ -97,16 +62,6 @@ struct Input : jasmin::Instruction<Input> {
auto [ptr] = in;
*ptr = static_cast<uint8_t>(std::getchar());
}

static void generate_code(jasmin::x64::CodeGenerator &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();
}
};

struct Output : jasmin::Instruction<Output> {
Expand All @@ -115,16 +70,6 @@ struct Output : jasmin::Instruction<Output> {
jasmin::Output<>) {
std::putchar(static_cast<char>(*in.get<0>()));
}

static void generate_code(jasmin::x64::CodeGenerator &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();
}
};

struct Zero : jasmin::Instruction<Zero> {
Expand All @@ -133,11 +78,6 @@ struct Zero : jasmin::Instruction<Zero> {
jasmin::Output<bool> out) {
out.set(*in.get<0>() == 0);
}

static void generate_code(jasmin::x64::CodeGenerator &gen,
jasmin::LocationMap const &) {
gen.write({0x8a, 0x06}); // mov al, BYTE PTR [rsi]
}
};

using Instructions =
Expand Down
11 changes: 7 additions & 4 deletions examples/brainfuck/jit.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
#include "examples/brainfuck/build.h"
#include "examples/brainfuck/file.h"
#include "examples/brainfuck/instructions.h"
#include "jasmin/compile/x64/code_generator.h"
#include "examples/brainfuck/x64_code_generator.h"
#include "jasmin/compile/compiled_function.h"
#include "jasmin/ssa/ssa.h"
#include "nth/dynamic/jit_function.h"

Expand All @@ -23,10 +24,12 @@ int main(int argc, char* argv[]) {

auto& f = std::get<jasmin::Function<bf::Instructions>>(fn_or_parse_error);

// Generate executable code for the function, storing it in `code`.
jasmin::CompiledFunction code;
jasmin::x64::CodeGenerator gen(nth::type<bf::Instructions>);
gen.function(jasmin::SsaFunction(f), code);
{ // Generate executable code for the function, storing it in `code`.
bf::X64CodeGenerator gen;
jasmin::x64::FunctionEmitter emitter(nth::type<bf::Instructions>, gen);
emitter.emit(jasmin::SsaFunction(f), code);
}

// Construct a JIT-compiled function from the code.
nth::jit_function<void()> jitted_fn(code);
Expand Down
76 changes: 76 additions & 0 deletions examples/brainfuck/x64_code_generator.h
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
6 changes: 3 additions & 3 deletions jasmin/compile/x64/BUILD
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package(default_visibility = ["//visibility:private"])

cc_library(
name = "code_generator",
hdrs = ["code_generator.h"],
srcs = ["code_generator.cc"],
name = "function_emitter",
hdrs = ["function_emitter.h"],
srcs = ["function_emitter.cc"],
visibility = ["//visibility:public"],
deps = [
":location_map",
Expand Down
74 changes: 74 additions & 0 deletions jasmin/compile/x64/function_emitter.cc
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
Loading

0 comments on commit b4c622a

Please sign in to comment.