Begin work on inline / builtin functions.

This commit is contained in:
Danila Fedorin 2018-07-28 16:17:06 -07:00
parent 5e93fa1963
commit 35b9c7651a
9 changed files with 164 additions and 55 deletions

21
src/chalk/builtin.cr Normal file
View File

@ -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

View File

@ -4,9 +4,12 @@ require "./emitter.cr"
module Chalk module Chalk
class CodeGenerator class CodeGenerator
include Emitter include Emitter
RETURN_REG = 14 RETURN_REG = 14
STACK_REG = 13 STACK_REG = 13
property instructions : Array(Instruction)
def initialize(table, @function : TreeFunction) def initialize(table, @function : TreeFunction)
@registers = 0 @registers = 0
@instructions = [] of Instruction @instructions = [] of Instruction
@ -18,25 +21,12 @@ module Chalk
end end
end end
def generate!(tree, table, target, free) def generate!(tree, function : InlineFunction, table, target, free)
case tree start = free
when TreeId function.generate!(self, tree.params, table, target, free)
entry = table[tree.id]? end
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 : TreeFunction | BuiltinFunction, table, target, free)
start_at = free start_at = free
# Move I to stack # Move I to stack
setis setis
@ -71,6 +61,28 @@ module Chalk
restore (start_at - 1) unless start_at == 0 restore (start_at - 1) unless start_at == 0
# Get call value into target # Get call value into target
loadr target, RETURN_REG 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 when TreeBlock
table = Table.new(table) table = Table.new(table)
tree.children.each do |child| tree.children.each do |child|
@ -110,13 +122,6 @@ module Chalk
return 0 return 0
end end
private def check_dead(inst)
if inst.is_a?(LoadRegInstruction)
return inst.from == inst.into
end
return false
end
def generate! def generate!
generate!(@function.block, @table, -1, @registers) generate!(@function.block, @table, -1, @registers)
return @instructions return @instructions

View File

@ -43,6 +43,9 @@ module Chalk
table[tree.name] = FunctionEntry.new tree table[tree.name] = FunctionEntry.new tree
end end
@logger.debug("Done creating symbol table") @logger.debug("Done creating symbol table")
table["draw"] = FunctionEntry.new InlineDrawFunction.new
table["get_key"] = FunctionEntry.new InlineAwaitKeyFunction.new
return table return table
end end
@ -52,6 +55,12 @@ module Chalk
return generator.generate! return generator.generate!
end 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) private def create_code(trees : Array(TreeFunction), table)
code = {} of String => Array(Instruction) code = {} of String => Array(Instruction)
trees.each do |tree| trees.each do |tree|
@ -97,14 +106,16 @@ module Chalk
while !open.empty? while !open.empty?
first = open.first first = open.first
open.delete first open.delete first
done << first
entry = table[first]? entry = table[first]?
raise "Unknown function" unless entry && entry.is_a?(FunctionEntry) 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 visitor = CallVisitor.new
entry.function.accept(visitor) function.accept(visitor)
open.concat(visitor.calls - done) open.concat(visitor.calls - done)
end end
return done return done
@ -118,14 +129,16 @@ module Chalk
names.delete "main" names.delete "main"
main_entry = table["main"]?.as(FunctionEntry) 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 main_entry.addr = 0
all_instructions << JumpRelativeInstruction.new 0 all_instructions << JumpRelativeInstruction.new 0
names.each do |name| names.each do |name|
entry = table[name]?.as(FunctionEntry) entry = table[name]?.as(FunctionEntry)
entry.addr = all_instructions.size 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 all_instructions << ReturnInstruction.new
end end

View File

@ -1,72 +1,72 @@
module Chalk module Chalk
module Emitter module Emitter
private def load(into, value) def load(into, value)
inst = LoadInstruction.new into, value.to_i32 inst = LoadInstruction.new into, value.to_i32
@instructions << inst @instructions << inst
return inst return inst
end end
private def loadr(into, from) def loadr(into, from)
inst = LoadRegInstruction.new into, from inst = LoadRegInstruction.new into, from
@instructions << inst @instructions << inst
return inst return inst
end end
private def op(op, into, from) def op(op, into, from)
inst = OpInstruction.new op, into, from inst = OpInstruction.new op, into, from
@instructions << inst @instructions << inst
return inst return inst
end end
private def opr(op, into, from) def opr(op, into, from)
inst = OpRegInstruction.new op, into, from inst = OpRegInstruction.new op, into, from
@instructions << inst @instructions << inst
return inst return inst
end end
private def sne(l, r) def sne(l, r)
inst = SkipNeInstruction.new l, r inst = SkipNeInstruction.new l, r
@instructions << inst @instructions << inst
return inst return inst
end end
private def jr(o) def jr(o)
inst = JumpRelativeInstruction.new o inst = JumpRelativeInstruction.new o
@instructions << inst @instructions << inst
return inst return inst
end end
private def store(up_to) def store(up_to)
inst = StoreInstruction.new up_to inst = StoreInstruction.new up_to
@instructions << inst @instructions << inst
return inst return inst
end end
private def restore(up_to) def restore(up_to)
inst = RestoreInstruction.new up_to inst = RestoreInstruction.new up_to
@instructions << inst @instructions << inst
return inst return inst
end end
private def ret def ret
inst = ReturnInstruction.new inst = ReturnInstruction.new
@instructions << inst @instructions << inst
return inst return inst
end end
private def call(func) def call(func)
inst = CallInstruction.new func inst = CallInstruction.new func
@instructions << inst @instructions << inst
return inst return inst
end end
private def setis def setis
inst = SetIStackInstruction.new inst = SetIStackInstruction.new
@instructions << inst @instructions << inst
return inst return inst
end end
private def addi(reg) def addi(reg)
inst = AddIRegInstruction.new reg inst = AddIRegInstruction.new reg
@instructions << inst @instructions << inst
return inst return inst

26
src/chalk/inline.cr Normal file
View File

@ -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

View File

@ -29,7 +29,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
0x6000 | (register << 8) | value 0x6000 | (@register << 8) | @value
end end
end end
@ -48,7 +48,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
0x8000 | (into << 8) | (from << 4) 0x8000 | (@into << 8) | (@from << 4)
end end
end end
@ -69,7 +69,7 @@ module Chalk
def to_bin(i, index) def to_bin(i, index)
case op case op
when TokenType::OpAdd when TokenType::OpAdd
return 0x7000 | (into << 8) | value return 0x7000 | (@into << 8) | @value
else else
raise "Invalid instruction" raise "Invalid instruction"
end end
@ -107,7 +107,7 @@ module Chalk
else else
raise "Invalid instruction" raise "Invalid instruction"
end end
return 0x8000 | (into << 8) | (from << 4) | code return 0x8000 | (@into << 8) | (@from << 4) | code
end end
end end
@ -123,7 +123,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
return 0xf055 | (up_to << 8) return 0xf055 | (@up_to << 8)
end end
end end
@ -139,7 +139,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
return 0xf065 | (up_to << 8) return 0xf065 | (@up_to << 8)
end end
end end
@ -167,7 +167,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
return 0x1000 | ((offset + index) * 2 + 0x200) return 0x1000 | ((@offset + index) * 2 + 0x200)
end end
end end
@ -185,7 +185,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
return 0x3000 | (left << 8) | right return 0x3000 | (@left << 8) | @right
end end
end end
@ -203,7 +203,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
return 0x4000 | (left << 8) | right return 0x4000 | (@left << 8) | @right
end end
end end
@ -222,7 +222,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
return 0x5000 | (left << 8) | (right << 4) return 0x5000 | (@left << 8) | (@right << 4)
end end
end end
@ -241,7 +241,7 @@ module Chalk
end end
def to_bin(i, index) def to_bin(i, index)
return 0x9000 | (left << 8) | (right << 4) return 0x9000 | (@left << 8) | (@right << 4)
end end
end end
@ -282,7 +282,40 @@ module Chalk
end end
def to_bin(i, index) 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 end
end end

View File

@ -4,6 +4,13 @@ module Chalk
@instructions = instructions.dup @instructions = instructions.dup
end end
private def check_dead(inst)
if inst.is_a?(LoadRegInstruction)
return inst.from == inst.into
end
return false
end
private def optimize!(range) private def optimize!(range)
offset = 0 offset = 0
range.each do |index| range.each do |index|

View File

@ -3,7 +3,7 @@ module Chalk
end end
class FunctionEntry < Entry class FunctionEntry < Entry
property function : TreeFunction property function : TreeFunction | BuiltinFunction | InlineFunction
property addr : Int32 property addr : Int32
def initialize(@function, @addr = -1) def initialize(@function, @addr = -1)

View File

@ -109,6 +109,10 @@ module Chalk
def initialize(@name, @params, @block) def initialize(@name, @params, @block)
end end
def param_count
return @params.size
end
def accept(v) def accept(v)
v.visit(self) v.visit(self)
@block.accept(v) @block.accept(v)