diff --git a/src/chalk/codegen.cr b/src/chalk/codegen.cr index 3f1b0f7..a9b6669 100644 --- a/src/chalk/codegen.cr +++ b/src/chalk/codegen.cr @@ -1,7 +1,9 @@ require "./ir.cr" +require "./emitter.cr" module Chalk class CodeGenerator + include Emitter RETURN_REG = 14 STACK_REG = 13 @@ -16,78 +18,6 @@ module Chalk 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) case tree when TreeId @@ -187,38 +117,8 @@ module Chalk return false 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! generate!(@function.block, @table, -1, @registers) - optimize! return @instructions end end diff --git a/src/chalk/compiler.cr b/src/chalk/compiler.cr index c933f68..984c9ac 100644 --- a/src/chalk/compiler.cr +++ b/src/chalk/compiler.cr @@ -6,40 +6,47 @@ module Chalk class Compiler def initialize(@config : Config) @logger = Logger.new STDOUT - @lexer = Lexer.new - @parser = Parser.new @logger.debug("Initialized compiler") @logger.level = Logger::DEBUG end private def create_trees(file) string = File.read(file) - tokens = @lexer.lex string + @logger.debug("Tokenizing") + lexer = Lexer.new + tokens = lexer.lex string if tokens.size == 0 && string != "" raise "Unable to tokenize file." end @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("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 end raise "Unable to parse file." end - private def process_initial(trees) + private def create_table(trees) table = Table.new - folder = ConstantFolder.new + @logger.debug("Creating symbol table") 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") table[tree.name] = FunctionEntry.new tree end + @logger.debug("Done creating symbol table") return table end - private def generate_code(trees, table) + private def create_code(trees, table) code = {} of String => Array(Instruction) trees.each do |tree| tree = tree.as(TreeFunction) @@ -58,15 +65,10 @@ module Chalk 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 - table, code = generate_ir + trees = create_trees(@config.file) + table = create_table(trees) + code = create_code(trees, table) code.each do |name, insts| puts "Code for #{name}:" insts.each { |it| puts it } @@ -74,12 +76,22 @@ module Chalk 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 private def run_binary 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"] table["main"]?.as(FunctionEntry).addr = 0 all_instructions << JumpRelativeInstruction.new 0 @@ -89,15 +101,8 @@ module Chalk all_instructions.concat(value) all_instructions << ReturnInstruction.new 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") - binary.each do |inst| - first = (inst >> 8).to_u8 - file.write_byte(first) - second = (inst & 0xff).to_u8 - file.write_byte(second) - end + generate_binary(table, all_instructions, file) file.close end diff --git a/src/chalk/emitter.cr b/src/chalk/emitter.cr new file mode 100644 index 0000000..d48dda4 --- /dev/null +++ b/src/chalk/emitter.cr @@ -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 diff --git a/src/chalk/optimizer.cr b/src/chalk/optimizer.cr new file mode 100644 index 0000000..6bb71ba --- /dev/null +++ b/src/chalk/optimizer.cr @@ -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 diff --git a/src/chalk/parser.cr b/src/chalk/parser.cr index 5075788..f7ac9d1 100644 --- a/src/chalk/parser.cr +++ b/src/chalk/parser.cr @@ -156,14 +156,14 @@ module Chalk params = arr[3..arr.size - 5].map &.as(Token).string code = arr[arr.size - 1].as(Tree) type = arr[arr.size - 2].as(Token).type - TreeFunction.new(name, params, code).as(Tree) + TreeFunction.new(name, params, code) end return func end def initialize _, 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 def parse?(tokens)