Refactor some code to split into functions and files.
This commit is contained in:
parent
cc82894454
commit
774ab2f92e
|
@ -1,7 +1,9 @@
|
||||||
require "./ir.cr"
|
require "./ir.cr"
|
||||||
|
require "./emitter.cr"
|
||||||
|
|
||||||
module Chalk
|
module Chalk
|
||||||
class CodeGenerator
|
class CodeGenerator
|
||||||
|
include Emitter
|
||||||
RETURN_REG = 14
|
RETURN_REG = 14
|
||||||
STACK_REG = 13
|
STACK_REG = 13
|
||||||
|
|
||||||
|
@ -16,78 +18,6 @@ module Chalk
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def load(into, value)
|
|
||||||
inst = LoadInstruction.new into, value.to_i32
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
private def loadr(into, from)
|
|
||||||
inst = LoadRegInstruction.new into, from
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
private def op(op, into, from)
|
|
||||||
inst = OpInstruction.new op, into, from
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
private def opr(op, into, from)
|
|
||||||
inst = OpRegInstruction.new op, into, from
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
private def sne(l, r)
|
|
||||||
inst = SkipNeInstruction.new l, r
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
private def jr(o)
|
|
||||||
inst = JumpRelativeInstruction.new o
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
private def store(up_to)
|
|
||||||
inst = StoreInstruction.new up_to
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
private def restore(up_to)
|
|
||||||
inst = RestoreInstruction.new up_to
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
private def ret
|
|
||||||
inst = ReturnInstruction.new
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
private def call(func)
|
|
||||||
inst = CallInstruction.new func
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
def setis
|
|
||||||
inst = SetIStackInstruction.new
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
def addi(reg)
|
|
||||||
inst = AddIRegInstruction.new reg
|
|
||||||
@instructions << inst
|
|
||||||
return inst
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate!(tree, table, target, free)
|
def generate!(tree, table, target, free)
|
||||||
case tree
|
case tree
|
||||||
when TreeId
|
when TreeId
|
||||||
|
@ -187,38 +117,8 @@ module Chalk
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
private def optimize!(range)
|
|
||||||
offset = 0
|
|
||||||
range.each do |index|
|
|
||||||
if check_dead(@instructions[index + offset])
|
|
||||||
@instructions.delete_at(index + offset)
|
|
||||||
offset -= 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return offset
|
|
||||||
end
|
|
||||||
|
|
||||||
private def optimize!
|
|
||||||
block_boundaries = [ @instructions.size ]
|
|
||||||
@instructions.each_with_index do |inst, i|
|
|
||||||
if inst.is_a?(JumpRelativeInstruction)
|
|
||||||
block_boundaries << (inst.offset + i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
block_boundaries.sort!
|
|
||||||
|
|
||||||
previous = 0
|
|
||||||
offset = 0
|
|
||||||
block_boundaries.each do |boundary|
|
|
||||||
range = (previous + offset)...(boundary + offset)
|
|
||||||
offset += optimize!(range)
|
|
||||||
previous = boundary
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate!
|
def generate!
|
||||||
generate!(@function.block, @table, -1, @registers)
|
generate!(@function.block, @table, -1, @registers)
|
||||||
optimize!
|
|
||||||
return @instructions
|
return @instructions
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,40 +6,47 @@ module Chalk
|
||||||
class Compiler
|
class Compiler
|
||||||
def initialize(@config : Config)
|
def initialize(@config : Config)
|
||||||
@logger = Logger.new STDOUT
|
@logger = Logger.new STDOUT
|
||||||
@lexer = Lexer.new
|
|
||||||
@parser = Parser.new
|
|
||||||
@logger.debug("Initialized compiler")
|
@logger.debug("Initialized compiler")
|
||||||
@logger.level = Logger::DEBUG
|
@logger.level = Logger::DEBUG
|
||||||
end
|
end
|
||||||
|
|
||||||
private def create_trees(file)
|
private def create_trees(file)
|
||||||
string = File.read(file)
|
string = File.read(file)
|
||||||
tokens = @lexer.lex string
|
@logger.debug("Tokenizing")
|
||||||
|
lexer = Lexer.new
|
||||||
|
tokens = lexer.lex string
|
||||||
if tokens.size == 0 && string != ""
|
if tokens.size == 0 && string != ""
|
||||||
raise "Unable to tokenize file."
|
raise "Unable to tokenize file."
|
||||||
end
|
end
|
||||||
@logger.debug("Finished tokenizing")
|
@logger.debug("Finished tokenizing")
|
||||||
if trees = @parser.parse?(tokens)
|
@logger.debug("Beginning parsing")
|
||||||
|
parser = Parser.new
|
||||||
|
if trees = parser.parse?(tokens)
|
||||||
@logger.debug("Finished parsing")
|
@logger.debug("Finished parsing")
|
||||||
|
@logger.debug("Beginning constant folding")
|
||||||
|
folder = ConstantFolder.new
|
||||||
|
trees.map! do |tree|
|
||||||
|
@logger.debug("Constant folding #{tree.name}")
|
||||||
|
tree.apply(folder).as(TreeFunction)
|
||||||
|
end
|
||||||
|
@logger.debug("Done constant folding")
|
||||||
return trees
|
return trees
|
||||||
end
|
end
|
||||||
raise "Unable to parse file."
|
raise "Unable to parse file."
|
||||||
end
|
end
|
||||||
|
|
||||||
private def process_initial(trees)
|
private def create_table(trees)
|
||||||
table = Table.new
|
table = Table.new
|
||||||
folder = ConstantFolder.new
|
@logger.debug("Creating symbol table")
|
||||||
trees.each do |tree|
|
trees.each do |tree|
|
||||||
tree = tree.as(TreeFunction)
|
|
||||||
@logger.debug("Constant folding #{tree.name}")
|
|
||||||
tree = tree.apply(folder).as(TreeFunction)
|
|
||||||
@logger.debug("Storing #{tree.name} in symbol table")
|
@logger.debug("Storing #{tree.name} in symbol table")
|
||||||
table[tree.name] = FunctionEntry.new tree
|
table[tree.name] = FunctionEntry.new tree
|
||||||
end
|
end
|
||||||
|
@logger.debug("Done creating symbol table")
|
||||||
return table
|
return table
|
||||||
end
|
end
|
||||||
|
|
||||||
private def generate_code(trees, table)
|
private def create_code(trees, table)
|
||||||
code = {} of String => Array(Instruction)
|
code = {} of String => Array(Instruction)
|
||||||
trees.each do |tree|
|
trees.each do |tree|
|
||||||
tree = tree.as(TreeFunction)
|
tree = tree.as(TreeFunction)
|
||||||
|
@ -58,15 +65,10 @@ module Chalk
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def generate_ir
|
|
||||||
trees = create_trees(@config.file)
|
|
||||||
table = process_initial(trees)
|
|
||||||
raise "No main function!" unless table["main"]?
|
|
||||||
return { table, generate_code(trees, table) }
|
|
||||||
end
|
|
||||||
|
|
||||||
private def run_intermediate
|
private def run_intermediate
|
||||||
table, code = generate_ir
|
trees = create_trees(@config.file)
|
||||||
|
table = create_table(trees)
|
||||||
|
code = create_code(trees, table)
|
||||||
code.each do |name, insts|
|
code.each do |name, insts|
|
||||||
puts "Code for #{name}:"
|
puts "Code for #{name}:"
|
||||||
insts.each { |it| puts it }
|
insts.each { |it| puts it }
|
||||||
|
@ -74,12 +76,22 @@ module Chalk
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def generate_binary(instructions)
|
private def generate_binary(table, instructions, dest)
|
||||||
|
context = InstructionContext.new table, instructions.size
|
||||||
|
binary = instructions.map_with_index { |it, i| it.to_bin(context, i).to_u16 }
|
||||||
|
binary.each do |inst|
|
||||||
|
first = (inst >> 8).to_u8
|
||||||
|
dest.write_byte(first)
|
||||||
|
second = (inst & 0xff).to_u8
|
||||||
|
dest.write_byte(second)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def run_binary
|
private def run_binary
|
||||||
all_instructions = [] of Instruction
|
all_instructions = [] of Instruction
|
||||||
table, code = generate_ir
|
trees = create_trees(@config.file)
|
||||||
|
table = create_table(trees)
|
||||||
|
code = create_code(trees, table)
|
||||||
all_instructions.concat code["main"]
|
all_instructions.concat code["main"]
|
||||||
table["main"]?.as(FunctionEntry).addr = 0
|
table["main"]?.as(FunctionEntry).addr = 0
|
||||||
all_instructions << JumpRelativeInstruction.new 0
|
all_instructions << JumpRelativeInstruction.new 0
|
||||||
|
@ -89,15 +101,8 @@ module Chalk
|
||||||
all_instructions.concat(value)
|
all_instructions.concat(value)
|
||||||
all_instructions << ReturnInstruction.new
|
all_instructions << ReturnInstruction.new
|
||||||
end
|
end
|
||||||
context = InstructionContext.new table, all_instructions.size
|
|
||||||
binary = all_instructions.map_with_index { |it, i| it.to_bin(context, i).to_u16 }
|
|
||||||
file = File.open("out.ch8", "w")
|
file = File.open("out.ch8", "w")
|
||||||
binary.each do |inst|
|
generate_binary(table, all_instructions, file)
|
||||||
first = (inst >> 8).to_u8
|
|
||||||
file.write_byte(first)
|
|
||||||
second = (inst & 0xff).to_u8
|
|
||||||
file.write_byte(second)
|
|
||||||
end
|
|
||||||
file.close
|
file.close
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
75
src/chalk/emitter.cr
Normal file
75
src/chalk/emitter.cr
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
module Chalk
|
||||||
|
module Emitter
|
||||||
|
private def load(into, value)
|
||||||
|
inst = LoadInstruction.new into, value.to_i32
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def loadr(into, from)
|
||||||
|
inst = LoadRegInstruction.new into, from
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def op(op, into, from)
|
||||||
|
inst = OpInstruction.new op, into, from
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def opr(op, into, from)
|
||||||
|
inst = OpRegInstruction.new op, into, from
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def sne(l, r)
|
||||||
|
inst = SkipNeInstruction.new l, r
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def jr(o)
|
||||||
|
inst = JumpRelativeInstruction.new o
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def store(up_to)
|
||||||
|
inst = StoreInstruction.new up_to
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def restore(up_to)
|
||||||
|
inst = RestoreInstruction.new up_to
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def ret
|
||||||
|
inst = ReturnInstruction.new
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def call(func)
|
||||||
|
inst = CallInstruction.new func
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def setis
|
||||||
|
inst = SetIStackInstruction.new
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
|
||||||
|
private def addi(reg)
|
||||||
|
inst = AddIRegInstruction.new reg
|
||||||
|
@instructions << inst
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
36
src/chalk/optimizer.cr
Normal file
36
src/chalk/optimizer.cr
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
module Chalk
|
||||||
|
class Optimizer
|
||||||
|
def initialize(instructions : Array(Instruction))
|
||||||
|
@instructions = instructions.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
private def optimize!(range)
|
||||||
|
offset = 0
|
||||||
|
range.each do |index|
|
||||||
|
if check_dead(@instructions[index + offset])
|
||||||
|
@instructions.delete_at(index + offset)
|
||||||
|
offset -= 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return offset
|
||||||
|
end
|
||||||
|
|
||||||
|
private def optimize!
|
||||||
|
block_boundaries = [ @instructions.size ]
|
||||||
|
@instructions.each_with_index do |inst, i|
|
||||||
|
if inst.is_a?(JumpRelativeInstruction)
|
||||||
|
block_boundaries << (inst.offset + i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
block_boundaries.sort!
|
||||||
|
|
||||||
|
previous = 0
|
||||||
|
offset = 0
|
||||||
|
block_boundaries.each do |boundary|
|
||||||
|
range = (previous + offset)...(boundary + offset)
|
||||||
|
offset += optimize!(range)
|
||||||
|
previous = boundary
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -156,14 +156,14 @@ module Chalk
|
||||||
params = arr[3..arr.size - 5].map &.as(Token).string
|
params = arr[3..arr.size - 5].map &.as(Token).string
|
||||||
code = arr[arr.size - 1].as(Tree)
|
code = arr[arr.size - 1].as(Tree)
|
||||||
type = arr[arr.size - 2].as(Token).type
|
type = arr[arr.size - 2].as(Token).type
|
||||||
TreeFunction.new(name, params, code).as(Tree)
|
TreeFunction.new(name, params, code)
|
||||||
end
|
end
|
||||||
return func
|
return func
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
_, block = create_statement_block
|
_, block = create_statement_block
|
||||||
@parser = many(create_func(block, create_type)).as(BasicParser(Array(Tree)))
|
@parser = many(create_func(block, create_type)).as(BasicParser(Array(TreeFunction)))
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse?(tokens)
|
def parse?(tokens)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user