From 51e451404c3a8a78180977ab0709c39a624b1eeb Mon Sep 17 00:00:00 2001 From: inglorion Date: Thu, 1 Oct 2009 10:23:17 +0200 Subject: [PATCH] More work on AMD64 code generator. --- lib/ruby/voodoo/generators/amd64_nasm_generator.rb | 148 +++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/lib/ruby/voodoo/generators/amd64_nasm_generator.rb b/lib/ruby/voodoo/generators/amd64_nasm_generator.rb index 97d6f54..7b32e79 100644 --- a/lib/ruby/voodoo/generators/amd64_nasm_generator.rb +++ b/lib/ruby/voodoo/generators/amd64_nasm_generator.rb @@ -40,6 +40,8 @@ module Voodoo def initialize params # Number of bytes in a word @WORDSIZE = 8 + # Word name in NASM lingo + @WORD_NAME = 'qword' # Default alignment for code @CODE_ALIGNMENT = 0 # Default alignment for data @@ -52,6 +54,10 @@ module Voodoo @SCRATCH_REG = 'r11' # Registers used for argument passing @ARG_REGS = ['rdi', 'rsi', 'rdx', 'rcx', 'r8', 'r9'] + # Base index + @BASE_INDEX = 'rbx' + # Count index + @COUNT_INDEX = 'rcx' super params end @@ -127,6 +133,53 @@ module Voodoo # == Loading Values # + # Create a value reference to an address. + # Invoking this code may clobber @BASE_INDEX and/or @COUNT_INDEX + def load_address base, offset, scale + base_ref = load_value base, @BASE_INDEX + offset_ref = load_value offset, @COUNT_INDEX + + if offset_ref == 0 + if integer? base_ref + # Only an integer base + "[#{base_ref}]" + else + # Some complex base; load in @BASE_INDEX + emit "mov #{@BASE_INDEX}, #{base_ref}\n" + "[#{@BASE_INDEX}]" + end + elsif base_ref == 0 + if integer? offset_ref + # Only a scaled offset + "[#{offset_ref.to_i * scale}]" + else + # Some complex offset; load in @COUNT_INDEX + emit "mov #{@COUNT_INDEX}, #{offset_ref}\n" + "[#{@COUNT_INDEX} * #{scale}]" + end + elsif integer? base_ref + if integer? offset_ref + # All integers, combine them together + "[#{base_ref.to_i + (offset_ref.to_i * scale)}]" + else + # Complex offset; use @COUNT_INDEX + emit "mov #{@COUNT_INDEX}, #{offset_ref}\n" + "[#{base_ref} + #{@COUNT_INDEX} * #{scale}]" + end + elsif integer? offset_ref + # Complex base, integer offset; use @BASE_INDEX + emit "mov #{@BASE_INDEX}, #{base_ref}\n" + "[#{@BASE_INDEX} + #{offset_ref.to_i * scale}]" + else + # Both base and offset are complex + # Use both @BASE_INDEX and @COUNT_INDEX + emit "mov #{@BASE_INDEX}, #{base_ref}\n" + emit "mov #{@COUNT_INDEX}, #{offset_ref}\n" + "[#{@BASE_INDEX} + #{@COUNT_INDEX} * #{scale}]" + end + end + + # Load the value of the nth argument def load_arg n, reg = @SCRATCH_REG if register_argument?(n) @@ -150,6 +203,88 @@ module Voodoo "[rbp - #{offset + n * wordsize}]" end + # Load a value into a register + def load_value_into_register value, register + value_ref = load_value x, register + set_register register, value_ref + end + + # + # == Expressions + # + + # Perform division. + # The quotient is stored in @ACC_INDEX, the remainder in @DATA_INDEX. + def eval_div x, y + x_ref = load_value_into_register x, @ACC_INDEX + y_ref = load_value y, @SCRATCH_REG + set_register @DATA_INDEX, 0 + emit "idiv #{@WORD_NAME} #{y_ref}\n" + end + + # Evaluate an expression. + # The result is stored in _register_ (@RETURN_REG by default). + def eval_expr words, register = @RETURN_REG + if words.length == 1 + if words[0] == 0 + emit "xor #{register}, #{register}\n" + else + load_value words[0], register + end + else + op = words[0] + case op + when :call + call *words[1..-1] + when :div + eval_div words[1], words[2] + set_register register, @ACC_INDEX + when :'get-byte' + # Clear register + set_register register, 0 + # Get address reference + address_ref = load_address words[1], words[2], 1 + # Load byte from address + case register + when 'eax', 'rax' + set_register 'al', address_ref + when 'ebx', 'rbx' + set_register 'bl', address_ref + when 'ecx', 'rcx' + set_register 'cl', address_ref + when 'edx', 'rdx' + set_register 'dl', address_ref + else + set_register @BASE_INDEX, 0 + set_register 'bl', address_ref + set_register register, @BASE_INDEX + end + when :'get-word' + address_ref = load_address words[1], words[2], @WORDSIZE + set_register register, address_ref + when :mod + eval_div words[1], words[2] + set_register register, @DATA_INDEX + when :mul + raise 'TODO' + eval_mul target_ref, words[1], words[2] + when :not + load_value_into_register words[1], register + emit "not #{register}\n" + else + raise 'TODO' + if binop?(op) + x_ref = load_value words[1], "edx" + y_ref = load_value words[2], "ebx" + emit "mov #{target_ref}, #{x_ref}\n" + emit "#{op} #{target_ref}, #{y_ref}\n" + else + raise "Not a magic word: #{words[0]}" + end + end + end + end + # # == Miscellaneous # @@ -182,6 +317,19 @@ module Voodoo def register_argument? n n < number_of_register_arguments end + + # Set a register to a value. + # The value must be a valid operand to the mov instruction. + def set_register register, value_ref + case value_ref + when register + # Nothing to do + when 0 + emit "xor #{register}, #{register}\n" + else + emit "mov #{register}, #{value_ref}\n" + end + end end # Register class -- 2.11.4.GIT