diff --git a/lib/vaporware/compiler.rb b/lib/vaporware/compiler.rb index bb270a6..417d772 100644 --- a/lib/vaporware/compiler.rb +++ b/lib/vaporware/compiler.rb @@ -9,12 +9,30 @@ def self.compile(source, compiler: "gcc", dest: "tmp", debug: false, compiler_op _precompile = "#{dest}.s" s = new(source, _precompile: _precompile, debug:, shared:) s.compile(compiler:, compiler_options:) + obj = s.assemble(input: _precompile, compmiler:, compiler_options:, debug:) + s.link(input: obj,link_options:) end def initialize(source, _precompile: "tmp.s", debug: false, shared: false) @generator = Vaporware::Compiler::Generator.new(source, precompile: _precompile, debug:, shared:) end + def assemble(input: precompile, output: File.basename(precompile, ".*") + ".o", compiler: "gcc", compiler_options: ["-O0"], debug: false) + base_name = File.basename(input, ".*") + name = shared ? "lib#{base_name}.so" : base_name + if compiler == "gcc" + compile_commands = [compiler, *compiler_options, "-o", name, input].compact + call_compiler(compile_commands) + else + Vaporware::Compiler::Assemble.assemble!(input, name) + end + + File.delete(input) unless debug + nil + end + + def call_compiler(compile_commands) = IO.popen(compile_commands).close + def compile(compiler: "gcc", compiler_options: ["-O0"]) @generator.register_var_and_method(@generator.ast) diff --git a/lib/vaporware/compiler/assemble.rb b/lib/vaporware/compiler/assemble.rb index ea28ca1..ff95099 100644 --- a/lib/vaporware/compiler/assemble.rb +++ b/lib/vaporware/compiler/assemble.rb @@ -1,17 +1,116 @@ # frozen_string_literal: true module Vaporware - class Compiler::Assemble - attr_reader :input, :output - def self.compile!(input, output = File.basename(input, ".*")) = new(input, output).compile + class Compiler + class Assemble + EXEC_ELF_HEADER = %w( + 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + 02 00 3e 00 01 00 00 00 78 00 40 00 00 00 00 00 + 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 40 00 38 00 01 00 40 00 00 00 00 00 + ).map { _1.to_i(16) }.pack("C*") - def initialize(input, output = File.basename(input, ".*")) - @input, @output = input, output - @target_file = File.open(output, "wb") - end + SHARED_ELF_HEADER = %w( + 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + 03 00 3e 00 01 00 00 00 78 00 40 00 00 00 00 00 + 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 40 00 38 00 01 00 40 00 00 00 00 00 + ).map { _1.to_i(16) }.pack("C*") + + PROGRAM_ELF_HEADER = %w( + 01 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 + 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 + 90 00 00 00 00 00 00 00 90 00 00 00 00 00 00 00 + 00 00 20 00 00 00 00 00 + ).map { _1.to_i(16) }.pack("C*") + + PREFIX = { + REX_W: "48", + } + + REGISTER_CODE = { + RAX: 0, + RDI: 7, + } + + attr_reader :input, :output + def self.assemble!(input, output = File.basename(input, ".*")) = new(input, output).assemble + + def initialize(input, output = File.basename(input, ".*") + ".o") + @input, @output = input, output + @target_file = File.open(output, "wb") + end + + def assemble(assemble_command: "as", assemble_options: [], input: @input, f: @target_file) + f.write(EXEC_ELF_HEADER) + f.write(PROGRAM_ELF_HEADER) + read = { main: false } + File.open(input, "r") do |r| + r.each_line do |line| + read[:main] = /main:/.match(line) unless read[:main] + next unless read[:main] && !/main:/.match(line) + op, *args = line.split(/\s+/).reject{ _1 == "" }.map { _1.gsub(/,/, "") } + p [op, args] + opes = opecode(op, args) + f.write(opes.pack("C*")) + end + end + f.close + f.path + end + + private + + def opecode(op, args) + case op + when "push" + push(args).map { _1.to_i(16) } + when "mov", "sub", "add" + [PREFIX[:REX_W], *mov_sub(op, *args)].map{ _1.to_i(16) } + when "pop" + pop(args) + end + end + + def mov_sub(*argments) + _op, *args = argments + args.map { reg(_1) } + end + + def push(args) + case args + in ["rbp"] | ["rdi"] + [0x50 + 5].map { _1.to_s(16) } + else + ["6a", *args] + end + end + + def pop(args) + case args + in ["rax"] | ["rdi"] + [0x58 + REGISTER_CODE[args.first.upcase.to_sym]] + end + end - def compile(compile_options = []) - self + def reg(r) + case r + in "rsp" + "89" + in "rbp" + "5e" + in "rax" + "29" + in "rdi" + "f8" + in /\d+/ + r + in "mov" + "89" + in "sub" + "83" + end + end end end end diff --git a/lib/vaporware/compiler/generator.rb b/lib/vaporware/compiler/generator.rb index 19f17d0..d620414 100644 --- a/lib/vaporware/compiler/generator.rb +++ b/lib/vaporware/compiler/generator.rb @@ -35,7 +35,7 @@ def to_elf(input: precompile, compiler: "gcc", compiler_options: ["-O0"], debug: base_name = File.basename(input, ".*") name = shared ? "lib#{base_name}.so" : base_name if compiler.nil? - Vaporware::Compiler::Assemble.compile!(name, input) + Vaporware::Compiler::Assemble.assemble!(name, input) else compile_commands = [compiler, *compiler_options, "-o", name, input].compact call_compiler(compile_commands) diff --git a/sig/vaporware/compiler.rbs b/sig/vaporware/compiler.rbs index 59e7d9c..e02a603 100644 --- a/sig/vaporware/compiler.rbs +++ b/sig/vaporware/compiler.rbs @@ -5,6 +5,8 @@ class Vaporware::Compiler @generator: Vaporware::Compiler::Generator # instance methods + def assemble: (?output: String, ?compiler: String, ?compiler_options: Array[String]) -> void + def link: (?output: String, ?linker: String, ?linker_options: Array[String]) -> void def compile: (?compiler: String, ?compiler_options: Array[String]) -> void end diff --git a/sig/vaporware/compiler/assemble.rbs b/sig/vaporware/compiler/assemble.rbs new file mode 100644 index 0000000..16e7d9d --- /dev/null +++ b/sig/vaporware/compiler/assemble.rbs @@ -0,0 +1,9 @@ +class Vaporware::Compiler::Assemble + attr_reader input: String + attr_reader output: String + + @target_file: File + + def initialize: (String, String) -> void + def compile: (?compiler_options: Array[String]) -> void +end diff --git a/sig/vaporware/compiler/generator.rbs b/sig/vaporware/compiler/generator.rbs index 3af1e8c..954a00d 100644 --- a/sig/vaporware/compiler/generator.rbs +++ b/sig/vaporware/compiler/generator.rbs @@ -22,7 +22,6 @@ class Vaporware::Compiler::Generator # instance private methods def already_build_methods?: -> bool - def call_compiler: (?output: String, ?compiler: String, ?compiler_options: Array[String]) -> void def call_method: (RubyVM::AbstractSyntaxTree::Node, File, bool) -> void def comp: (String, File) -> void def compile_shared_option: () -> Array[String]