Skip to content

Commit

Permalink
Merge branch 'main' into compile-to-elf
Browse files Browse the repository at this point in the history
  • Loading branch information
katsyoshi authored Jun 4, 2023
2 parents 3d09733 + 8245dfe commit 6ee9714
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 119 deletions.
4 changes: 2 additions & 2 deletions lib/vaporware/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ def compile(compiler: "gcc", compiler_options: ["-O0"])
output.puts " push rbp"
output.puts " mov rbp, rsp"
output.puts " sub rsp, #{@generator.defined_variables.size * 8}"
@generator.build(@generator.ast, output)
@generator.to_asm(@generator.ast, output)
# epilogue
@generator.epilogue(output)
else
@generator.prologue_methods(output)
output.puts ".globl main" unless @generator.shared
@generator.build(@generator.ast, output)
@generator.to_asm(@generator.ast, output)
# epilogue
@generator.epilogue(output)
end
Expand Down
86 changes: 38 additions & 48 deletions lib/vaporware/compiler/generator.rb
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
# frozen_string_literal: true
require "parser/current"

module Vaporware
class Compiler
class Generator
REGISTER = %w(rdi rsi rdx rcx r8 r9)
attr_accessor :main
attr_reader :ast, :precompile, :debug, :seq, :defined_variables, :doned, :shared, :defined_methods
def initialize(source, precompile:, debug:, shared:)
@precompile, @debug, @shared = precompile, debug, shared
@doned, @defined_methods, @defined_variables = Set.new, Set.new, Set.new
@seq, @main = 0, false
src = File.read(File.expand_path(source))
@ast = Parser::CurrentRuby.parse(src)
@ast = RubyVM::AbstractSyntaxTree.parse_file(source)
end

def compile_shared_option = %w(-shared -fPIC)

def register_var_and_method(node)
return unless node.kind_of?(Parser::AST::Node)
return unless node.kind_of?(RubyVM::AbstractSyntaxTree::Node)
type = node.type
if variable_or_method?(type)
name, _ = node.children
name = [:lvasgn, :arg].include?(type) ? "lvar_#{name}".to_sym : name
type == :def ? @defined_methods << name : @defined_variables << name
variables, *_ = node.children
case type
when :SCOPE
variables.each { |v| @defined_variables << v }
when :DEFN
@defined_methods << variables
end
node.children.each { |n| register_var_and_method(n) }
nil
end

def already_build_methods? = defined_methods.sort == @doned.to_a.sort
def variable_or_method?(type) = [:lvasgn, :arg, :def].include?(type)

def to_elf(input: precompile, compiler: "gcc", compiler_options: ["-O0"], debug: false)
base_name = File.basename(input, ".*")
Expand Down Expand Up @@ -75,24 +76,14 @@ def method(method, node, output)
output.puts "#{method}:"
define_method_prologue(node, output)
node.children.each do |child|
next unless child.kind_of?(Parser::AST::Node)
build(child, output, true)
next unless child.kind_of?(RubyVM::AbstractSyntaxTree::Node)
to_asm(child, output, true)
end
ret(output)
@doned << method
nil
end

def args(node, output)
node.children.each do |child|
name = "arg_#{child.children.first}".to_sym
lvar(name, output)
output.puts " pop rax"
output.puts " mov rax, [rax]"
output.puts " push rax"
end
end

def call_method(node, output, method_tree)
output.puts " mov rax, rsp"
output.puts " mov rdi, 16"
Expand All @@ -107,7 +98,7 @@ def call_method(node, output, method_tree)
output.puts " push rax"
_, name, *args = node.children
args.each_with_index do |arg, i|
build(arg, output, method_tree)
to_asm(arg, output, method_tree)
output.puts " pop #{REGISTER[i]}"
end

Expand Down Expand Up @@ -152,14 +143,17 @@ def ret(output)
output.puts " ret"
end

def build(node, output, method_tree = false)
return unless node.kind_of?(Parser::AST::Node)
def to_asm(node, output, method_tree = false)
return unless node.kind_of?(RubyVM::AbstractSyntaxTree::Node)
type = node.type
center = case type
when :int
when :LIT
output.puts " push #{node.children.last}"
return
when :begin
when :LIST, :BLOCK
node.children.each { |n| to_asm(n, output, method_tree) }
return
when :SCOPE
node.children.each do |child|
if already_build_methods? && !@main
return if shared
Expand All @@ -170,82 +164,78 @@ def build(node, output, method_tree = false)
output.puts " push rax"
@main = true
end
build(child, output)
to_asm(child, output)
end
return
when :def
when :DEFN
name, _ = node.children
method(name, node, output)
return
when :args
args(node, output)
return
when :lvar
when :LVAR
return if method_tree
name = "lvar_#{node.children.last}".to_sym
name = node.children.last
lvar(name, output)
# lvar
output.puts " pop rax"
output.puts " mov rax, [rax]"
output.puts " push rax"
return
when :lvasgn
left, right = node.children
when :LASGN
name, right = node.children

# rvar
name = "lvar_#{left}".to_sym
lvar(name, output)
build(right, output, method_tree)
to_asm(right, output, method_tree)

output.puts " pop rdi"
output.puts " pop rax"
output.puts " mov [rax], rdi"
output.puts " push rdi"
output.puts " pop rax"
return
when :if
when :IF
cond, tblock, fblock = node.children
build(cond, output)
to_asm(cond, output)
output.puts " pop rax"
output.puts " push rax"
output.puts " cmp rax, 0"
if fblock
output.puts " je .Lelse#{seq}"
build(tblock, output, method_tree)
to_asm(tblock, output, method_tree)
ret(output)
output.puts " jmp .Lend#{seq}"
output.puts ".Lelse#{seq}:"
build(fblock, output, method_tree)
to_asm(fblock, output, method_tree)
ret(output)
output.puts ".Lend#{seq}:"
else
output.puts " je .Lend#{seq}"
build(tblock, output, method_tree)
to_asm(tblock, output, method_tree)
ret(output)
output.puts ".Lend#{seq}:"
end
@seq += 1
return
when :while
when :WHILE
cond, tblock = node.children
output.puts ".Lbegin#{seq}:"
build(cond, output, method_tree)
to_asm(cond, output, method_tree)
output.puts " pop rax"
output.puts " push rax"
output.puts " cmp rax, 0"
output.puts " je .Lend#{seq}"
build(tblock, output, method_tree)
to_asm(tblock, output, method_tree)
output.puts " jmp .Lbegin#{seq}"
output.puts ".Lend#{seq}:"
@seq += 1
return
when :send
when :OPCALL
left, center, right = node.children
build(left, output, method_tree) unless left.nil?
to_asm(left, output, method_tree) unless left.nil?
if left.nil?
call_method(node, output, method_tree)
else
build(right, output, method_tree)
to_asm(right, output, method_tree)
output.puts " pop rdi"
end
output.puts " pop rax"
Expand Down
2 changes: 1 addition & 1 deletion rbs_collection.lock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
sources:
- type: git
name: ruby/gem_rbs_collection
revision: 12631190e75c631eca0908ecc8c03bcfd78c0c99
revision: 28208148c7e64a25e9b86b9723b4c3a2cef14e81
remote: https://github.com/ruby/gem_rbs_collection.git
repo_dir: gems
path: ".gem_rbs_collection"
Expand Down
1 change: 0 additions & 1 deletion rbs_collection.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ gems:
ignore: true
- name: vaporware
ignore: true
- name: parser
- name: set
ignore: true
67 changes: 0 additions & 67 deletions sig/vaporware.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,4 @@ module Vaporware
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
class Error
end
class Compiler
# class methods
def self.compile: (String, ?compiler: String, ?dest: String, ?debug: bool, ?compiler_options: Array[String], ?shared: bool) -> nil
def initialize: (String, ?_precompile: String, ?debug: bool, ?shared: bool) -> untyped
@generator: Vaporware::Compiler::Generator
# instance methods
def compile: (?compiler: String, ?compiler_options: Array[String]) -> nil

class Assemble
@input: String
@output: String
@target_file: File
# class methods
def self.compile!: (String, ?String) -> Vaporware::Compiler::Assemble
def initialize: (String, ?String) -> untyped

# instance methods
def compile: (?Array[String]) -> self
end

class Generator
REGISTER: Array[String]
# instance variables
@precompile: String
@debug: bool
@doned: Set[Symbol]
@defined_methods: Set[Symbol]
@defined_variables: Set[Symbol]
@seq: Integer
@main: bool
@shared: bool
# temporarily using untyped types since parser.gem's rbs information is unchecked.
@ast: untyped
# class methods
def initialize: (String, precompile: String, debug: bool, shared: bool) -> untyped

# attr reader for instance variables
def precompile: -> String
def ast: -> untyped # Parser::AST::Node
def defined_methods: -> Set[Symbol]
def defined_variables: -> Set[Symbol]
def debug: -> bool
def doned: -> Set[Symbol]
def seq: -> Integer
def shared: -> bool

# instance methods
def already_build_methods?: -> bool
def args: (untyped, File) -> nil
def build: (untyped, File, ?bool) -> nil
def call_compiler: (Array[String]) -> nil
def call_method: (untyped, File, bool) -> nil
def comp: (String, File) -> nil
def compile_shared_option: () -> Array[String]
def define_method_prologue: (untyped, File) -> nil
def epilogue: (File) -> nil
def lvar: (untyped, File) -> nil
def method: (Symbol, untyped, File) -> nil
def prologue: (untyped, File) -> nil
def prologue_methods: (File) -> nil
def ret: (File) -> nil
def lvar_offset: (Symbol | nil) -> Integer
def register_var_and_method: (untyped) -> nil
def to_elf: (?input: String, ?compiler: String, ?compiler_options: Array[String], ?debug: bool) -> nil
def variable_or_method?: (Symbol) -> bool
end
end
end
10 changes: 10 additions & 0 deletions sig/vaporware/compiler.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class Vaporware::Compiler
# class methods
def self.compile: (String, ?compiler: String, ?dest: String, ?debug: bool, ?compiler_options: Array[String], ?shared: bool) -> void
def initialize: (String, ?_precompile: String, ?debug: bool, ?shared: bool) -> void

@generator: Vaporware::Compiler::Generator
# instance methods
def compile: (?compiler: String, ?compiler_options: Array[String]) -> void
end

40 changes: 40 additions & 0 deletions sig/vaporware/compiler/generator.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class Vaporware::Compiler::Generator
REGISTER: Array[String]

# attr_accessor and define instance variables
@main: bool
attr_accessor main (): bool

# attr_reader for instance variables
attr_reader debug: bool
attr_reader doned: Set[Symbol]
attr_reader defined_methods: Set[Symbol]
attr_reader defined_variables: Set[Symbol]
attr_reader precompile: String
attr_reader seq: Integer
attr_reader shared: bool

# temporarily using untyped types since parser.gem's rbs information is unchecked.
attr_reader ast: RubyVM::AbstractSyntaxTree::Node

# class methods
def initialize: (String, precompile: String, debug: bool, shared: bool) -> void

# 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]
def define_method_prologue: (RubyVM::AbstractSyntaxTree::Node, File) -> void
def epilogue: (File) -> void
def lvar: (Symbol, File) -> void
def lvar_offset: (Symbol | nil) -> Integer
def method: (Symbol, RubyVM::AbstractSyntaxTree::Node, File) -> void
def prologue: (RubyVM::AbstractSyntaxTree::Node, File) -> void
def prologue_methods: (File) -> void
def register_var_and_method: (RubyVM::AbstractSyntaxTree::Node) -> void
def ret: (File) -> void
def to_asm: (RubyVM::AbstractSyntaxTree::Node, File, ?bool) -> void
def variable_or_method?: (Symbol) -> bool
end

0 comments on commit 6ee9714

Please sign in to comment.