diff --git a/src/chalk.cr b/src/chalk.cr index 0443cb5..226b0bc 100644 --- a/src/chalk.cr +++ b/src/chalk.cr @@ -5,7 +5,6 @@ module Chalk config = Config.parse! exit unless config.validate! - generator = CodeGenerator.new (Table.new) compiler = Compiler.new config compiler.run end diff --git a/src/chalk/codegen.cr b/src/chalk/codegen.cr index 402d21f..4f9b84b 100644 --- a/src/chalk/codegen.cr +++ b/src/chalk/codegen.cr @@ -2,53 +2,81 @@ require "./ir.cr" module Chalk class CodeGenerator - def initialize(@table : Table) + def initialize(table, @function : TreeFunction) @register = 0 @instructions = [] of Instruction - @block_edges = [] of Int32 + @table = Table.new table + + @function.params.each do |param| + @table[param] = VarEntry.new @register + @register += 1 + end end private def load(into, value) - @instructions << LoadInstruction.new into, value.to_i32 + inst = LoadInstruction.new into, value.to_i32 + @instructions << inst + return inst end private def loadr(into, from) - @instructions << LoadRegInstruction.new into, from + inst = LoadRegInstruction.new into, from + @instructions << inst + return inst end private def op(op, into, from) - @instructions << OpRegInstruction.new op, into, from + inst = OpRegInstruction.new op, into, from + @instructions << inst + return inst + end + + private def jeq(rel, l, r) + inst = JumpEqRegInstruction.new rel, l, r + @instructions << inst + return inst end private def store(up_to) - @instructions << StoreInstruction.new up_to + inst = StoreInstruction.new up_to + @instructions << inst + return inst end private def restore(up_to) - @instructions << RestoreInstruction.new up_to + inst = RestoreInstruction.new up_to + @instructions << inst + return inst end private def ret(reg) - @instructions << ReturnInstruction.new reg + inst = ReturnInstruction.new reg + @instructions << inst + return inst end - def generate(tree : Tree, target : Int32) + def generate!(tree, target) case tree when TreeId entry = @table[tree.id]? raise "Unknown variable" unless entry && entry.is_a?(VarEntry) - loadr target, entry.as(VarEntry).register + loadr target, entry.register when TreeLit load target, tree.lit when TreeOp - generate tree.left, target - generate tree.right, @register - op tree.op, target, @register + into = @register + @register += 1 + generate! tree.left, target + generate! tree.right, into + @register -= 1 + op tree.op, target, into when TreeBlock + register = @register tree.children.each do |child| - generate child, @register + generate! child, @register end + @register = register when TreeVar entry = @table[tree.name]? if entry == nil @@ -57,22 +85,75 @@ module Chalk @table[tree.name] = entry end raise "Unknown variable" unless entry.is_a?(VarEntry) - generate tree.expr, entry.register + generate! tree.expr, entry.register when TreeAssign entry = @table[tree.name]? raise "Unknown variable" unless entry && entry.is_a?(VarEntry) - generate tree.expr, entry.as(VarEntry).register + generate! tree.expr, entry.register + when TreeIf + cond_target = @register + @register += 1 + + generate! tree.condition, cond_target + load cond_target + 1, 0 + jump_inst = jeq 0, cond_target, cond_target + 1 + @register -= 1 + + old_size = @instructions.size + generate! tree.block, @register + jump_inst.offset = @instructions.size - old_size + 1 + + generate! tree.otherwise, @register if tree.otherwise when TreeReturn - generate tree.rvalue, target - ret target + into = @register + @register += 1 + generate! tree.rvalue, into + @register -= 1 + ret into end end - def generate(tree : Tree) - generate(tree, 0) - @block_edges << @instructions.size - @block_edges.sort! + private def check_dead(inst) + if inst.is_a?(LoadRegInstruction) + return inst.from == inst.into + end + 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?(JumpEqRegInstruction) || + inst.is_a?(JumpEqInstruction) + 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, @register) + optimize! return @instructions end end diff --git a/src/chalk/compiler.cr b/src/chalk/compiler.cr index 840b1c0..0ad5f9f 100644 --- a/src/chalk/compiler.cr +++ b/src/chalk/compiler.cr @@ -40,11 +40,15 @@ module Chalk end private def generate_code(trees, table) + code = {} of String => Array(Instruction) trees.each do |tree| - generator = CodeGenerator.new table - instructions = generator.generate tree.as(TreeFunction).block - instructions.each { |it| puts it } + tree = tree.as(TreeFunction) + generator = CodeGenerator.new table, tree + @logger.debug("Generating code for #{tree.name}") + instructions = generator.generate! + code[tree.name] = instructions end + return code end private def run_tree @@ -57,7 +61,13 @@ module Chalk private def run_intermediate trees = create_trees(@config.file) table = process_initial(trees) - generate_code(trees, table) + raise "No main function!" unless table["main"]? + code = generate_code(trees, table) + code.each do |name, insts| + puts "Code for #{name}:" + insts.each { |it| puts it } + puts "-----" + end end private def run_binary diff --git a/src/chalk/ir.cr b/src/chalk/ir.cr index a1959b5..b0cac24 100644 --- a/src/chalk/ir.cr +++ b/src/chalk/ir.cr @@ -8,7 +8,7 @@ module Chalk property register : Int32 property value : Int32 - def initialize(@register : Int32, @value : Int32) + def initialize(@register, @value) end def to_s(io) @@ -22,7 +22,7 @@ module Chalk property into : Int32 property from : Int32 - def initialize(@into : Int32, @from : Int32) + def initialize(@into, @from) end def to_s(io) @@ -38,8 +38,7 @@ module Chalk property into : Int32 property value : Int32 - def initialize(@op : TokenType, @into : Int32, - @value : Int32) + def initialize(@op, @into, @value) end def to_s(io) @@ -54,7 +53,7 @@ module Chalk property into : Int32 property from : Int32 - def initialize(@op : TokenType, @into : Int32, @from : Int32) + def initialize(@op, @into, @from) end def to_s(io) @@ -68,7 +67,7 @@ module Chalk class StoreInstruction < Instruction property up_to : Int32 - def initialize(@up_to : Int32) + def initialize(@up_to) end def to_s(io) @@ -80,7 +79,7 @@ module Chalk class RestoreInstruction < Instruction property up_to : Int32 - def initialize(@up_to : Int32) + def initialize(@up_to) end def to_s(io) @@ -92,7 +91,7 @@ module Chalk class ReturnInstruction < Instruction property to_return : Int32 - def initialize(@to_return : Int32) + def initialize(@to_return) end def to_s(io) @@ -100,4 +99,36 @@ module Chalk @to_return.to_s(16, io) end end + + class JumpEqInstruction < Instruction + property offset : Int32 + property left : Int32 + property right : Int32 + + def initialize(@offset, @left, @right) + end + + def to_s(io) + io << "jeq " << offset << " R" + @left.to_s(16, io) + io << " " << right + end + end + + class JumpEqRegInstruction < Instruction + property offset : Int32 + property left : Int32 + property right : Int32 + + def initialize(@offset, @left, @right) + end + + def to_s(io) + io << "jeq " << offset << " R" + @left.to_s(16, io) + io << " R" + @right.to_s(16, io) + end + end + end diff --git a/src/chalk/parsers.cr b/src/chalk/parsers.cr index 8d530a7..5ea58de 100644 --- a/src/chalk/parsers.cr +++ b/src/chalk/parsers.cr @@ -5,8 +5,7 @@ module Chalk abstract def parse?(tokens : Array(Token), index : Int64) : Tuple(T, Int64)? - def parse(tokens : Array(Token), - index : Int64) : Tuple(T, Int64) + def parse(tokens, index) return parse?(tokens, index).not_nil! end diff --git a/src/chalk/table.cr b/src/chalk/table.cr index 387b80a..db3611d 100644 --- a/src/chalk/table.cr +++ b/src/chalk/table.cr @@ -6,7 +6,7 @@ module Chalk property function : TreeFunction property addr : Int32 - def initialize(@function : TreeFunction, @addr : Int32 = -1) + def initialize(@function, @addr = -1) end def to_s(io) @@ -17,7 +17,7 @@ module Chalk class VarEntry < Entry property register : Int32 - def initialize(@register : Int32) + def initialize(@register) end def to_s(io) @@ -28,11 +28,11 @@ module Chalk class Table property parent : Table? - def initialize(@parent : Table? = nil) + def initialize(@parent = nil) @data = {} of String => Entry end - def []?(key) : Entry? + def []?(key) if entry = @data[key]? return entry end diff --git a/src/chalk/tree.cr b/src/chalk/tree.cr index 624c48e..8fa223e 100644 --- a/src/chalk/tree.cr +++ b/src/chalk/tree.cr @@ -1,25 +1,25 @@ module Chalk class Visitor - def visit(tree : Tree) + def visit(tree) end - def finish(tree : Tree) + def finish(tree) end end class Transformer - def transform(tree : Tree) : Tree + def transform(tree) return tree end end class Tree - def accept(v : Visitor) + def accept(v) v.visit(self) v.finish(self) end - def apply(t : Transformer) + def apply(t) return t.transform(self) end end @@ -27,14 +27,14 @@ module Chalk class TreeId < Tree property id : String - def initialize(@id : String) + def initialize(@id) end end class TreeLit < Tree property lit : Int64 - def initialize(@lit : Int64) + def initialize(@lit) end end @@ -42,16 +42,16 @@ module Chalk property name : String property params : Array(Tree) - def initialize(@name : String, @params : Array(Tree)) + def initialize(@name, @params) end - def accept(v : Visitor) + def accept(v) v.visit(self) @params.each &.accept(v) v.finish(self) end - def apply(t : Transformer) + def apply(t) @params.map! do |param| param.apply(t) end @@ -64,17 +64,17 @@ module Chalk property left : Tree property right : Tree - def initialize(@op : TokenType, @left : Tree, @right : Tree) + def initialize(@op, @left, @right) end - def accept(v : Visitor) + def accept(v) v.visit(self) @left.accept(v) @right.accept(v) v.finish(self) end - def apply(t : Transformer) + def apply(t) @left = @left.apply(t) @right = @right.apply(t) return t.transform(self) @@ -84,16 +84,16 @@ module Chalk class TreeBlock < Tree property children : Array(Tree) - def initialize(@children : Array(Tree)) + def initialize(@children) end - def accept(v : Visitor) + def accept(v) v.visit(self) @children.each &.accept(v) v.finish(self) end - def apply(t : Transformer) + def apply(t) @children.map! do |child| child.apply(t) end @@ -106,16 +106,16 @@ module Chalk property params : Array(String) property block : Tree - def initialize(@name : String, @params : Array(String), @block : Tree) + def initialize(@name, @params, @block) end - def accept(v : Visitor) + def accept(v) v.visit(self) @block.accept(v) v.finish(self) end - def apply(t : Transformer) + def apply(t) @block = @block.apply(t) return t.transform(self) end @@ -125,16 +125,16 @@ module Chalk property name : String property expr : Tree - def initialize(@name : String, @expr : Tree) + def initialize(@name, @expr) end - def accept(v : Visitor) + def accept(v) v.visit(self) @expr.accept(v) v.finish(self) end - def apply(t : Transformer) + def apply(t) @expr = @expr.apply(t) return t.transform(self) end @@ -144,16 +144,16 @@ module Chalk property name : String property expr : Tree - def initialize(@name : String, @expr : Tree) + def initialize(@name, @expr) end - def accept(v : Visitor) + def accept(v) v.visit(self) @expr.accept(v) v.finish(self) end - def apply(t : Transformer) + def apply(t) @expr = @expr.apply(t) return t.transform(self) end @@ -164,10 +164,10 @@ module Chalk property block : Tree property otherwise : Tree? - def initialize(@condition : Tree, @block : Tree, @otherwise : Tree? = nil) + def initialize(@condition, @block, @otherwise = nil) end - def accept(v : Visitor) + def accept(v) v.visit(self) @condition.accept(v) @block.accept(v) @@ -175,7 +175,7 @@ module Chalk v.finish(self) end - def apply(t : Transformer) + def apply(t) @condition = @condition.apply(t) @block = @block.apply(t) @otherwise = @otherwise.try &.apply(t) @@ -187,17 +187,17 @@ module Chalk property condition : Tree property block : Tree - def initialize(@condition : Tree, @block : Tree) + def initialize(@condition, @block) end - def accept(v : Visitor) + def accept(v) v.visit(self) @condition.accept(v) @block.accept(v) v.finish(self) end - def apply(t : Transformer) + def apply(t) @condition = @condition.apply(t) @block = @block.apply(t) return t.transform(self) @@ -207,16 +207,16 @@ module Chalk class TreeReturn < Tree property rvalue : Tree - def initialize(@rvalue : Tree) + def initialize(@rvalue) end - def accept(v : Visitor) + def accept(v) v.visit(self) @rvalue.accept(v) v.finish(self) end - def apply(t : Transformer) + def apply(t) @rvalue = @rvalue.apply(t) return t.transform(self) end diff --git a/src/chalk/ui.cr b/src/chalk/ui.cr index 2bce834..15f812f 100644 --- a/src/chalk/ui.cr +++ b/src/chalk/ui.cr @@ -9,7 +9,7 @@ module Chalk property file : String property mode : OutputMode - def initialize(@file : String = "", + def initialize(@file = "", @mode = OutputMode::Tree) end