Added support for div, mod, and mul to amd64 code generator.
[voodoo-lang.git] / lib / ruby / voodoo / generators / nasm_generator.rb
bloba6948b7faf92797ef84698d04b09152defe71209
1 require 'voodoo/generators/common_code_generator'
3 module Voodoo
4   # Mixin for code generators that target NASM
5   #
6   # Requires the following methods and constants:
7   # - #emit
8   # - #emit_function_prologue
9   # - #load_arg
10   # - #load_local
11   # - @CODE_ALIGNMENT
12   # - @DATA_ALIGNMENT
13   # - @FUNCTION_ALIGNMENT
14   # - @SCRATCH_REG
15   class NasmGenerator < CommonCodeGenerator
17     #
18     # == Labels
19     #
21     # Export symbols from the current section
22     def export *symbols
23       emit "global #{symbols.join ', '}\n"
24     end
26     # Import labels into the current section
27     def import *symbols
28       emit "extern #{symbols.join ', '}\n"
29     end
31     # Define a label in the current section
32     def label name
33       emit "#{name}:\n"
34     end
36     #
37     # == Data definition
38     #
40     # Define a byte with the given value
41     def byte value
42       emit "db #{value}\n"
43     end
45     # Define a dword with the given value
46     def dword value
47       emit "dd #{value}\n"
48     end
50     # Define a qword with the given value
51     def qword value
52       emit "dq #{value}\n"
53     end
55     # Define a string with the given value
56     def string value
57       code = ''
58       in_quote = false
59       value.each_byte do |b|
60         if b >= 32 && b < 128
61           if in_quote
62             code << b.chr
63           else
64             code << "'" + b.chr
65             in_quote = true
66           end
67         else
68           if in_quote
69             code << "',#{b}"
70             in_quote = false
71           else
72             code << ",#{b}"
73           end
74         end
75       end
76       emit "db #{code}\n"
77     end
79     #
80     # == Alignment
81     #
83     def align_data alignment = @DATA_ALIGNMENT
84       in_section(:data) { emit "align #{alignment},db 0" }
85     end
87     def align_code alignment = @CODE_ALIGNMENT
88       in_section(:code) { emit "align #{alignment},nop" }
89     end
91     def align_function alignment = @FUNCTION_ALIGNMENT
92       in_section(:code) { emit "align #{alignment},nop" }
93     end
95     #
96     # == Functions
97     #
99     # Emit function preamble and declare _formals_ as function arguments
100     def begin_function *formals
101       emit "; function #{formals.join ' '}\n"
102       environment = Environment.new @environment
103       environment.add_args formals
104       @environment = environment
105       emit_function_prologue formals
106     end
108     # End a function body
109     def end_function
110       emit "; end function\n\n"
111       if @environment == @top_level
112         raise "Cannot end function when not in a function"
113       else
114         @environment = @top_level
115       end
116     end
118     #
119     # == Conditionals
120     #
122     # End a conditional body
123     def end_if
124       label = @if_labels.pop
125       emit "#{label}:\n"
126     end
128     #
129     # == Value Classification
130     #
132     # Test if op is a binary operation
133     def binop? op
134       [:div, :mod, :sub].member?(op) || symmetric_operation?(op)
135     end
137     # Test if a value is an integer
138     def integer? value
139       value.kind_of? Integer
140     end
142     # Test if a symbol refers to a global
143     def global? symbol
144       symbol?(symbol) && @environment[symbol] == nil
145     end
147     # Tests if an operand is an immediate operand
148     def immediate_operand? operand
149       integer?(operand) || global?(operand)
150     end
152     # Tests if an operand is a memory operand
153     def memory_operand? operand
154       operand[0] == ?[
155     end
157     # Test if a value is a symbol
158     def symbol? value
159       value.kind_of? Symbol
160     end
162     # Test if op is a symmetric operation (i.e. it will yield the
163     # same result if the order of its source operands is changed).
164     def symmetric_operation? op
165       [:add, :and, :mul, :or, :xor].member? op
166     end
168     #
169     # == Loading Values
170     #
172     # Load the value associated with the given symbol.
173     # Returns a string that can be used to refer to the loaded value.
174     def load_symbol symbol, reg = @SCRATCH_REG
175       x = @environment[symbol]
176       if x
177         case x[0]
178         when :arg
179           load_arg x[1], reg
180         when :local
181           load_local x[1], reg
182         else
183           raise "Invalid variable type: #{x[0]}"
184         end
185       else
186         # Assume global
187         symbol
188       end
189     end
190     
191     # Load a value.
192     # Returns a string that can be used to refer to the loaded value.
193     def load_value value, reg = @SCRATCH_REG
194       if integer? value
195         # Integers can be used as is
196         value
197       elsif symbol? value
198         load_symbol value, reg
199       end
200     end
202     #
203     # == Miscellaneous
204     #
206     # Emit a comment
207     def comment text
208       emit ";#{text}\n"
209     end
211     #
212     # == Output
213     #
215     # Write generated code to the given IO object.
216     def write io
217       io.puts "bits #{@WORDSIZE * 8}\n\n"
218       @sections.each do |section,code|
219         unless code.empty?
220           case section
221           when :code
222             section_name = '.text'
223           when :data
224             section_name = '.data'
225           when :functions
226             section_name = '.text'
227           else
228             section_name = section.to_s
229           end
230           io.puts "section #{section_name}"
231           io.puts code
232           io.puts
233         end
234       end
235     end
237   end