require "tempfile"
def emit(text)
- $asm_out.write "\t#{text}\n"
+ $asm_out.write " #{text}\n"
end
+START_CODE = <<-eos
+.text
+.global _start
+_start:
+ movl (%rsp), %edi
+ lea 8(%rsp), %rsi
+ call main
+ movl %eax, %edi
+ movl $60, %eax
+ syscall
+
+eos
module Targets
module X86_64
############
# FUNCTIONS
############
- # call NA
def call
emit "call #{name}"
end
- # call_and_ret
def call_and_ret
emit "call #{name}"
emit "pushq %rax"
end
-# def ret
-# end
-#
-# def ret_with_val
-# end
-
- def syscall num_args
+ def scall num_args
+ $syscalls[num_args] = true
emit "call syscall#{num_args}"
+ emit "pushq %rax"
end
#########
def byte(value)
emit ".byte 0x#{"%02x" % value.to_i}"
end
-
- private
-
- def emit(text)
- $asm_out.write "\t#{text}\n"
- end
-
end
class Function
- def initialize(name, args=[], returns = nil, &block)
+ def initialize(name, nargs = 0, &block)
+ @nargs = nargs
+ @returned = false
$asm_out.write ".text\n"
$asm_out.write "#{name}:\n"
emit "pushq %rbp"
emit "movq %rsp, %rbp"
-# emit ";-------------------"
instance_eval(&block)
-# emit ";-------------------"
- emit "popq %rax" if returns
- emit "popq %rbp"
- emit "ret"
$asm_out.write "\n"
+ raise "functions require at least one return instruction" if not @returned
end
def method_missing(m, *args, &block)
$asm_out.write "subq $#{count * 8}, %rsp\n"
end
+ def ret
+ emit "popq %rbp"
+ if @nargs > 0 then
+ emit "ret $#{@nargs * 8}"
+ else
+ emit "ret"
+ end
+ @returned = true
+ end
-# ##
-# # Literal Value Loading
-# ##
-#
-# def int(value)
-# puts "\tmovq\t$#{value}, %rax"
-# puts "\tpushq\t%rax"
-# end
-#
-# def float(value)
-## puts "\tmovq\t$#{value}, %rax"
-## puts "\tpushq\t%rax"
-# end
-#
-# def bool(value)
-# puts "\tmovq\t$#{value}, %rax"
-# puts "\tpushq\t%rax"
-# end
-#
-# def byte(value)
-# puts "\tmovq\t$#{value}, %rax"
-# puts "\tpushq\t%rax"
-# end
-
-
-# ##
-# # Local/Global Variable Access
-# ##
-#
-# def local_get(idx)
-# raise "unimplemented"
-# end
-#
-# def local_set(idx)
-# raise "unimplemented"
-# end
-#
-# def global_get(idx)
-# raise "unimplemented"
-# end
-#
-# def global_set(idx)
-# raise "unimplemented"
-# end
-#
-# def ret()
-# puts "\tret"
-# end
-#
-# def ret_val()
-# puts "\tret"
-# end
-
+ def retwv
+ emit "popq %rax"
+ ret
+ end
def var(name, &block)
raise "variable declarations not allowed inside function block"
end
end
-#def label(name, &block)
-# puts "name:"
-# block.call()
-#end
-
-def prog_start func
- $asm_out.write ".text\n"
- $asm_out.write ".global _start\n"
- $asm_out.write "_start:\n"
- emit "movl (%rsp), %edi\n"
- emit "lea 8(%rsp), %rsi\n"
- emit "call #{func || main}\n"
- emit "movl %eax, %edi\n"
- emit "movl $60, %eax\n"
- emit "syscall\n"
- $asm_out.write ""
-end
-
def var(name, &block)
Variable.new(name, &block)
end
-def func(name, args=[], returns = nil, &block)
- Function.new(name, args, returns, &block)
+def func(name, nargs=0, &block)
+ Function.new(name, nargs, &block)
+ $main_obj = true if name == :main
end
def target(name)
$target = Targets.const_get(name.to_s)
end
-# Process all the files
+def gen_syscall_handler(n)
+ regs = %w[%rdi %rsi %rdx %r10 %r8 %r9]
+ $asm_out.write ".text\n"
+ $asm_out.write ".global syscall#{n}\n"
+ $asm_out.write "syscall#{n}:\n"
+ emit "movq 8(%rsp), %rax"
+ n.times do |i|
+ emit "movq #{(n+1)*8}(%rsp), #{regs[i]}"
+ end
+ emit "syscall"
+ emit "ret $#{(n+1) * 8}"
+ $asm_out.write "\n"
+end
+
+# Setup assembler global state
+$main_obj = false
+$syscalls = [false, false, false, false, false, false, false]
$target = Targets::X86_64
$out_name = ARGV[0]
$asm_out = Tempfile.new("aas")
+
+# Process all the input files
ARGV[1..-1].each do |f|
- puts "loading #{f}"
load f
end
+$syscalls.each_with_index do |used, n|
+ gen_syscall_handler(n) if used
+end
+$asm_out.write START_CODE if $main_obj
+
+# Now run it through the system assembler
$asm_out.close
-#puts File.read($asm_out.path)
+puts File.read($asm_out.path)
system "as -o #{$out_name} #{$asm_out.path}"
+$asm_out.unlink
+