diff --git a/src/chalk/builtin.cr b/src/chalk/builtin.cr new file mode 100644 index 0000000..561a25d --- /dev/null +++ b/src/chalk/builtin.cr @@ -0,0 +1,21 @@ +module Chalk + class BuiltinFunction + getter param_count : Int32 + + def initialize(@param_count) + end + + def generate!(into) + end + end + + class InlineFunction + getter param_count : Int32 + + def initialize(@param_count) + end + + def generate!(codegen, params, table, target, free) + end + end +end diff --git a/src/chalk/codegen.cr b/src/chalk/codegen.cr index 0a54bc8..7075b39 100644 --- a/src/chalk/codegen.cr +++ b/src/chalk/codegen.cr @@ -4,9 +4,12 @@ require "./emitter.cr" module Chalk class CodeGenerator include Emitter + RETURN_REG = 14 STACK_REG = 13 + property instructions : Array(Instruction) + def initialize(table, @function : TreeFunction) @registers = 0 @instructions = [] of Instruction @@ -18,25 +21,12 @@ module Chalk end end - def generate!(tree, table, target, free) - case tree - when TreeId - entry = table[tree.id]? - raise "Unknown variable" unless entry && - entry.is_a?(VarEntry) - loadr target, entry.register - when TreeLit - load target, tree.lit - when TreeOp - generate! tree.left, table, target, free - generate! tree.right, table, free, free + 1 - opr tree.op, target, free - when TreeCall - entry = table[tree.name]? - raise "Unknown function" unless entry && - entry.is_a?(FunctionEntry) - raise "Invalid call" if tree.params.size != entry.function.params.size + def generate!(tree, function : InlineFunction, table, target, free) + start = free + function.generate!(self, tree.params, table, target, free) + end + def generate!(tree, function : TreeFunction | BuiltinFunction, table, target, free) start_at = free # Move I to stack setis @@ -71,6 +61,28 @@ module Chalk restore (start_at - 1) unless start_at == 0 # Get call value into target loadr target, RETURN_REG + end + + def generate!(tree, table, target, free) + case tree + when TreeId + entry = table[tree.id]? + raise "Unknown variable" unless entry && + entry.is_a?(VarEntry) + loadr target, entry.register + when TreeLit + load target, tree.lit + when TreeOp + generate! tree.left, table, target, free + generate! tree.right, table, free, free + 1 + opr tree.op, target, free + when TreeCall + entry = table[tree.name]? + raise "Unknown function" unless entry && + entry.is_a?(FunctionEntry) + function = entry.function + raise "Invalid call" if tree.params.size != function.param_count + generate! tree, function, table, target, free when TreeBlock table = Table.new(table) tree.children.each do |child| @@ -110,13 +122,6 @@ module Chalk return 0 end - private def check_dead(inst) - if inst.is_a?(LoadRegInstruction) - return inst.from == inst.into - end - return false - end - def generate! generate!(@function.block, @table, -1, @registers) return @instructions diff --git a/src/chalk/compiler.cr b/src/chalk/compiler.cr index 605f3ae..843edd7 100644 --- a/src/chalk/compiler.cr +++ b/src/chalk/compiler.cr @@ -43,6 +43,9 @@ module Chalk table[tree.name] = FunctionEntry.new tree end @logger.debug("Done creating symbol table") + + table["draw"] = FunctionEntry.new InlineDrawFunction.new + table["get_key"] = FunctionEntry.new InlineAwaitKeyFunction.new return table end @@ -52,6 +55,12 @@ module Chalk return generator.generate! end + private def create_code(tree : BuiltinFunction, table) + instructions = [] of Instruction + tree.generate!(instructions) + return instructions + end + private def create_code(trees : Array(TreeFunction), table) code = {} of String => Array(Instruction) trees.each do |tree| @@ -97,14 +106,16 @@ module Chalk while !open.empty? first = open.first open.delete first - done << first entry = table[first]? raise "Unknown function" unless entry && entry.is_a?(FunctionEntry) - next unless entry.function.is_a?(TreeFunction) + function = entry.function + next if function.is_a?(InlineFunction) + done << first + next unless function.is_a?(TreeFunction) visitor = CallVisitor.new - entry.function.accept(visitor) + function.accept(visitor) open.concat(visitor.calls - done) end return done @@ -118,14 +129,16 @@ module Chalk names.delete "main" main_entry = table["main"]?.as(FunctionEntry) - all_instructions.concat create_code(main_entry.function, table) + all_instructions.concat create_code(main_entry.function.as(TreeFunction), table) main_entry.addr = 0 all_instructions << JumpRelativeInstruction.new 0 names.each do |name| entry = table[name]?.as(FunctionEntry) entry.addr = all_instructions.size - all_instructions.concat create_code(entry.function, table) + function = entry.function + raise "Trying to compile inlined function" if function.is_a?(InlineFunction) + all_instructions.concat create_code(function, table) all_instructions << ReturnInstruction.new end diff --git a/src/chalk/emitter.cr b/src/chalk/emitter.cr index e2f4c7d..1d3cdf4 100644 --- a/src/chalk/emitter.cr +++ b/src/chalk/emitter.cr @@ -1,72 +1,72 @@ module Chalk module Emitter - private def load(into, value) + def load(into, value) inst = LoadInstruction.new into, value.to_i32 @instructions << inst return inst end - private def loadr(into, from) + def loadr(into, from) inst = LoadRegInstruction.new into, from @instructions << inst return inst end - private def op(op, into, from) + def op(op, into, from) inst = OpInstruction.new op, into, from @instructions << inst return inst end - private def opr(op, into, from) + def opr(op, into, from) inst = OpRegInstruction.new op, into, from @instructions << inst return inst end - private def sne(l, r) + def sne(l, r) inst = SkipNeInstruction.new l, r @instructions << inst return inst end - private def jr(o) + def jr(o) inst = JumpRelativeInstruction.new o @instructions << inst return inst end - private def store(up_to) + def store(up_to) inst = StoreInstruction.new up_to @instructions << inst return inst end - private def restore(up_to) + def restore(up_to) inst = RestoreInstruction.new up_to @instructions << inst return inst end - private def ret + def ret inst = ReturnInstruction.new @instructions << inst return inst end - private def call(func) + def call(func) inst = CallInstruction.new func @instructions << inst return inst end - private def setis + def setis inst = SetIStackInstruction.new @instructions << inst return inst end - private def addi(reg) + def addi(reg) inst = AddIRegInstruction.new reg @instructions << inst return inst diff --git a/src/chalk/inline.cr b/src/chalk/inline.cr new file mode 100644 index 0000000..b971fbd --- /dev/null +++ b/src/chalk/inline.cr @@ -0,0 +1,26 @@ +module Chalk + class InlineDrawFunction < InlineFunction + def initialize + @param_count = 3 + end + + def generate!(emitter, params, table, target, free) + if !params[2].is_a?(TreeLit) + raise "Third parameter must be a constant." + end + emitter.generate! params[0], table, free, free + 1 + emitter.generate! params[1], table, free + 1, free + 2 + emitter.instructions << DrawInstruction.new free, free + 1, params[2].as(TreeLit).lit.to_i32 + end + end + + class InlineAwaitKeyFunction < InlineFunction + def initialize + @param_count = 0 + end + + def generate!(emitter, params, table, target, free) + emitter.instructions << AwaitKeyInstruction.new target + end + end +end diff --git a/src/chalk/ir.cr b/src/chalk/ir.cr index 73b9ecf..4252768 100644 --- a/src/chalk/ir.cr +++ b/src/chalk/ir.cr @@ -29,7 +29,7 @@ module Chalk end def to_bin(i, index) - 0x6000 | (register << 8) | value + 0x6000 | (@register << 8) | @value end end @@ -48,7 +48,7 @@ module Chalk end def to_bin(i, index) - 0x8000 | (into << 8) | (from << 4) + 0x8000 | (@into << 8) | (@from << 4) end end @@ -69,7 +69,7 @@ module Chalk def to_bin(i, index) case op when TokenType::OpAdd - return 0x7000 | (into << 8) | value + return 0x7000 | (@into << 8) | @value else raise "Invalid instruction" end @@ -107,7 +107,7 @@ module Chalk else raise "Invalid instruction" end - return 0x8000 | (into << 8) | (from << 4) | code + return 0x8000 | (@into << 8) | (@from << 4) | code end end @@ -123,7 +123,7 @@ module Chalk end def to_bin(i, index) - return 0xf055 | (up_to << 8) + return 0xf055 | (@up_to << 8) end end @@ -139,7 +139,7 @@ module Chalk end def to_bin(i, index) - return 0xf065 | (up_to << 8) + return 0xf065 | (@up_to << 8) end end @@ -167,7 +167,7 @@ module Chalk end def to_bin(i, index) - return 0x1000 | ((offset + index) * 2 + 0x200) + return 0x1000 | ((@offset + index) * 2 + 0x200) end end @@ -185,7 +185,7 @@ module Chalk end def to_bin(i, index) - return 0x3000 | (left << 8) | right + return 0x3000 | (@left << 8) | @right end end @@ -203,7 +203,7 @@ module Chalk end def to_bin(i, index) - return 0x4000 | (left << 8) | right + return 0x4000 | (@left << 8) | @right end end @@ -222,7 +222,7 @@ module Chalk end def to_bin(i, index) - return 0x5000 | (left << 8) | (right << 4) + return 0x5000 | (@left << 8) | (@right << 4) end end @@ -241,7 +241,7 @@ module Chalk end def to_bin(i, index) - return 0x9000 | (left << 8) | (right << 4) + return 0x9000 | (@left << 8) | (@right << 4) end end @@ -282,7 +282,40 @@ module Chalk end def to_bin(i, index) - return 0xf000 | (reg << 8) | 0x1e + return 0xf000 | (@reg << 8) | 0x1e + end + end + + class DrawInstruction < Instruction + property x : Int32 + property y : Int32 + property height : Int32 + + def initialize(@x, @y, @height) + end + + def to_s(io) + io << "draw R" + x.to_s(16, io) + io << " R" + y.to_s(16, io) + io << " " << height + end + + def to_bin(i, index) + return 0xd000 | (@x << 8) | (@y << 4) | height + end + end + + class AwaitKeyInstruction < Instruction + property into : Int32 + + def initialize(@into) + end + + def to_s(io) + io << "getk R" + @into.to_s(16, io) end end end diff --git a/src/chalk/optimizer.cr b/src/chalk/optimizer.cr index 6eaafa6..0a27f4f 100644 --- a/src/chalk/optimizer.cr +++ b/src/chalk/optimizer.cr @@ -4,6 +4,13 @@ module Chalk @instructions = instructions.dup end + 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| diff --git a/src/chalk/table.cr b/src/chalk/table.cr index db3611d..7d3492e 100644 --- a/src/chalk/table.cr +++ b/src/chalk/table.cr @@ -3,7 +3,7 @@ module Chalk end class FunctionEntry < Entry - property function : TreeFunction + property function : TreeFunction | BuiltinFunction | InlineFunction property addr : Int32 def initialize(@function, @addr = -1) diff --git a/src/chalk/tree.cr b/src/chalk/tree.cr index 8fa223e..250eaf9 100644 --- a/src/chalk/tree.cr +++ b/src/chalk/tree.cr @@ -109,6 +109,10 @@ module Chalk def initialize(@name, @params, @block) end + def param_count + return @params.size + end + def accept(v) v.visit(self) @block.accept(v)