Browse files

Additional refactoring

  • Loading branch information...
vidarh committed Sep 28, 2014
1 parent 2da30b5 commit 083d0b576aaf1f9c92a1612fb75db4ce236d1447
Showing with 3 additions and 230 deletions.
  1. +3 −230 compiler.rb
@@ -16,12 +16,15 @@
require 'compile_arithmetic'
require 'compile_comparisons'
require 'compile_calls'
require 'compile_class'
require 'compile_control'
require 'trace'
require 'stackfence'
require 'saveregs'
require 'splat'
require 'value'
require 'output_functions'
class Compiler
attr_reader :global_functions
@@ -158,67 +161,6 @@ def output_constants
# Similar to output_constants, but for functions.
# Compiles all functions, defined so far and outputs the appropriate assembly code.
def output_functions
# This is a bit ugly, but handles the case of lambdas or inner
# functions being added during the compilation... Should probably
# refactor.
while f = @global_functions.shift
name = f[0]
func = f[1]
# create a function scope for each defined function and compile it appropriately.
# also pass it the current global scope for further lookup of variables used
# within the functions body that aren't defined there (global variables and those,
# that are defined in the outer scope of the function's)
fscope =
pos = func.body.respond_to?(:position) ? func.body.position : nil
fname = pos ? pos.filename : nil
@e.include(fname) do
# We extract the usage frequency information and pass it to the emitter
# to inform the register allocation.
varfreq = func.body.respond_to?(:extra) ? func.body.extra[:varfreq] : []
@e.func(name, pos, varfreq) do
minargs = func.minargs
compile_if(fscope, [:lt, :numargs, minargs],
[:sexp,[:call, :printf,
["ArgumentError: In %s - expected a minimum of %d arguments, got %d\n",
name, minargs - 2, [:sub, :numargs,2]]], [:div,1,0] ])
if !
maxargs = func.maxargs
compile_if(fscope, [:gt, :numargs, maxargs],
[:sexp,[:call, :printf,
["ArgumentError: In %s - expected a maximum of %d arguments, got %d\n",
name, maxargs - 2, [:sub, :numargs,2]]], [:div,1,0] ])
if func.defaultvars > 0
@e.with_stack(func.defaultvars) do
func.process_defaults do |arg, index|
@e.comment("Default argument for #{} at position #{2 + index}")
compile_if(fscope, [:lt, :numargs, 1 + index],
[:assign, ("#", arg.default],
[:assign, ("#",])
compile_eval_arg(fscope, func.body)
@e.comment("Reloading self if evicted:")
# Ensure %esi is intact on exit, if needed:
# Need to clean up the name to be able to use it in the assembler.
# Strictly speaking we don't *need* to use a sensible name at all,
# but it makes me a lot happier when debugging the asm.
@@ -276,80 +218,6 @@ def compile_defun(scope, name, args, body)
return[:addr, clean_method_name(name)])
# Compiles a method definition and updates the
# class vtable.
def compile_defm(scope, name, args, body)
scope = scope.class_scope
# FIXME: Replace "__closure__" with the block argument name if one is present
f =,[:self,:__closure__]+args, body, scope) # "self" is "faked" as an argument to class methods
@e.comment("method #{name}")
cleaned = clean_method_name(name)
fname = "__method_#{}_#{cleaned}"
scope.set_vtable_entry(name, fname, f)
# Save to the vtable.
v = scope.vtable[name]
compile_eval_arg(scope,[:sexp, [:call, :__set_vtable, [:self,v.offset, fname.to_sym]]])
# add the method to the global list of functions defined so far
# with its "munged" name.
@global_functions[fname] = f
# This is taken from compile_defun - it does not necessarily make sense for defm
return[:addr, clean_method_name(fname)])
def compile_jmp_on_false(scope, r, target)
if r && r.type == :object
@e.cmpl(@e.result_value, "nil")
@e.cmpl(@e.result_value, "false")
@e.jmp_on_false(target, r)
# Compiles an if expression.
# Takes the current (outer) scope and two expressions representing
# the if and else arm.
# If no else arm is given, it defaults to nil.
def compile_if(scope, cond, if_arm, else_arm = nil)
@e.comment("if: #{cond.inspect}")
res = compile_eval_arg(scope, cond)
l_else_arm = @e.get_local + "_else"
compile_jmp_on_false(scope, res, l_else_arm)
@e.comment("then: #{if_arm.inspect}")
ifret = compile_eval_arg(scope, if_arm)
l_end_if_arm = @e.get_local + "_endif" if else_arm
@e.comment("else: #{else_arm.inspect}")
elseret = compile_eval_arg(scope, else_arm) if else_arm
@e.local(l_end_if_arm) if else_arm
# At the moment, we're not keeping track of exactly what might have gone on
# in the if vs. else arm, so we need to assume all bets are off.
combine_types(ifret, elseret)
def compile_return(scope, arg = nil)
@e.save_result(compile_eval_arg(scope, arg)) if arg
def compile_rescue(scope, *args)
warning("RESCUE is NOT IMPLEMENTED")[:subexpr])
@@ -373,37 +241,6 @@ def combine_types(left, right)
# Changes to make #compile_if comply with real-life requirements
# makes it hard to use it to implement 'or' without introducing a
# temporarily variable. First we did that using a global, as a
# hack. This does things more "properly" as a first stage to
# either refactoring out the commonalities with compile_if or
# create a "lower level" more generic method to handle conditions
# (for "or" we really only need a way to explicitly say that
# the return value of the condition should be left untouched
# if the "true" / if-then part of the the if condition should remain
def compile_or scope, left, right
@e.comment("compile_or: #{left.inspect} || #{right.inspect}")
ret = compile_eval_arg(scope,left)
l_or = @e.get_local + "_or"
compile_jmp_on_false(scope, ret, l_or)
l_end_or = @e.get_local + "_end_or"
@e.comment(".. or:")
or_ret = compile_eval_arg(scope,right)
# Compiles the ternary if form (cond ? then : else)
# It may be better to transform this into the normal
# if form in the tree.
@@ -651,17 +488,6 @@ def compile_index(scope, arr, index)
# Compiles a while loop.
# Takes the current scope, a condition expression as well as the body of the function.
def compile_while(scope, cond, body)
@e.loop do |br|
var = compile_eval_arg(scope, cond)
compile_jmp_on_false(scope, var, br)
compile_exp(scope, body)
# Compiles a let expression.
# Takes the current scope, a list of variablenames as well as a list of arguments.
def compile_let(scope, varlist, *args)
@@ -683,59 +509,6 @@ def compile_let(scope, varlist, *args)
def compile_module(scope,name, *exps)
# FIXME: This is a cop-out that will cause horrible
# crashes - they are not the same (though nearly)
compile_class(scope,name, *exps)
# Compiles a class definition.
# Takes the current scope, the name of the class as well as a list of expressions
# that belong to the class.
def compile_class(scope, name,superclass, *exps)
superc = name == :Class ? nil : @classes[superclass]
cscope = scope.find_constant(name)
@e.comment("=== class #{} ===")
name =
# The check for :Class and :Kernel is an "evil" temporary hack to work around the bootstrapping
# issue of creating these class objects before Object is initialized. A better solution (to avoid
# demanding an explicit order would be to clear the Object constant and make sure __new_class_object
#does not try to deref a null pointer
sscope = (name == superclass or name == :Class or name == :Kernel) ? nil : @classes[superclass]
ssize = sscope ? sscope.klass_size : nil
ssize = 0 if ssize.nil?
compile_exp(scope, [:assign, name.to_sym, [:sexp,[:call, :__new_class_object, [cscope.klass_size,superclass,ssize]]]])
@global_constants << name
# In the context of "cscope", "self" refers to the Class object of the newly instantiated class.
# Previously we used "@instance_size" directly instead of [:index, :self, 1], but when fixing instance
# variable handling and adding offsets to avoid overwriting instance variables in the superclass,
# this broke, as obviously we should not be able to directly mess with the superclass's instance
# variables, so we're intentionally violating encapsulation here.
compile_exp(cscope, [:assign, [:index, :self, 1], cscope.instance_size])
# We need to store the "raw" name here, rather than a String object,
# as String may not have been initialized yet
compile_exp(cscope, [:assign, [:index, :self, 2], name.to_s])
exps.each do |e|
addr = compile_do(cscope, *e)
@e.comment("=== end class #{name} ===")
return[:global, name], :object)
# Put at the start of a required file, to allow any special processing
# before/after
def compile_required(scope,exp)

0 comments on commit 083d0b5

Please sign in to comment.