voodoo 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,14 +66,14 @@ end
66
66
  if architecture == :help
67
67
  # List supported architectures
68
68
  puts "Supported architectures:"
69
- puts Voodoo::CodeGenerator.architectures
69
+ puts Voodoo::CodeGenerator.architectures.map{|arch| arch.to_s}.sort
70
70
  exit
71
71
  end
72
72
 
73
73
  if output_format == :help
74
74
  # List supported output formats
75
75
  puts "Supported output formats for architecture #{architecture}:"
76
- puts Voodoo::CodeGenerator.formats architecture
76
+ puts Voodoo::CodeGenerator.formats(architecture).map{|f| f.to_s}.sort
77
77
  exit
78
78
  end
79
79
 
@@ -4,14 +4,47 @@ module Voodoo
4
4
  # Class that holds configuration parameters
5
5
  class Configuration
6
6
  def initialize
7
- @default_architecture = :amd64
7
+ @default_architecture = :auto
8
8
  @default_format = :elf
9
+ @gas_command = "/usr/bin/as"
9
10
  @nasm_command = "/usr/bin/nasm"
10
11
  end
11
12
 
12
- attr_accessor :default_architecture,
13
- :default_format,
13
+ attr_accessor :default_format,
14
+ :gas_command,
14
15
  :nasm_command
16
+
17
+ attr_writer :default_architecture
18
+
19
+ # Returns the default architecture for this compiler
20
+ def default_architecture
21
+ if @default_architecture == :auto
22
+ # If @default_architecture has been set to :auto,
23
+ # return the host architecture, falling back to :i386
24
+ # if the host architecture cannot be determined
25
+ host_architecture || :i386
26
+ else
27
+ @default_architecture
28
+ end
29
+ end
30
+
31
+ # Returns the architecture of the machine running the program,
32
+ # or nil if that architecture cannot be determined
33
+ def host_architecture
34
+ arch, = RUBY_PLATFORM.split '-'
35
+ case arch
36
+ when 'x86_64', 'amd64'
37
+ :amd64
38
+ when 'i386', 'i686'
39
+ :i386
40
+ when 'mips'
41
+ :mips
42
+ when 'mipsel'
43
+ :mipsel
44
+ else
45
+ nil
46
+ end
47
+ end
15
48
  end
16
49
 
17
50
  DEFAULT_CONFIGURATION = Configuration.new
@@ -32,6 +65,13 @@ module Voodoo
32
65
  def default_format= value
33
66
  DEFAULT_CONFIGURATION.default_format = value
34
67
  end
68
+ def gas_command
69
+ DEFAULT_CONFIGURATION.gas_command
70
+ end
71
+
72
+ def gas_command= value
73
+ DEFAULT_CONFIGURATION.gas_command = value
74
+ end
35
75
  def nasm_command
36
76
  DEFAULT_CONFIGURATION.nasm_command
37
77
  end
@@ -13,8 +13,12 @@ module Voodoo
13
13
  end
14
14
 
15
15
  def output_file_name input_name
16
- input_name.sub(/\.voo$/, '') + '.o'
17
- end
16
+ @elfgenerator.output_file_name input_name
17
+ end
18
+
19
+ def output_file_suffix
20
+ @elfgenerator.output_file_suffix
21
+ end
18
22
 
19
23
  def write io
20
24
  @elfgenerator.write io
@@ -96,7 +96,7 @@ module Voodoo
96
96
  def call func, *args
97
97
  emit "; call #{func} #{args.join ' '}\n"
98
98
  # First couple of arguments go in registers
99
- register_args = args[0..number_of_register_arguments] || []
99
+ register_args = args[0..(number_of_register_arguments - 1)] || []
100
100
  # Rest of arguments go on the stack
101
101
  stack_args = args[number_of_register_arguments..-1] || []
102
102
  emit "; register_args: #{register_args.inspect}\n"
@@ -175,24 +175,23 @@ module Voodoo
175
175
 
176
176
  # Set stack arguments
177
177
  if args.length > number_of_register_arguments
178
- (args.length - 1 .. number_of_register_arguments).each do |i|
178
+ (args.length - 1).downto(number_of_register_arguments).each do |i|
179
179
  arg = args[i]
180
180
 
181
181
  value_ref = load_value arg, @SCRATCH_REG
182
182
  newarg_ref = load_arg i
183
183
  # Elide code if source is same as destination
184
184
  unless value_ref == newarg_ref
185
- emit "mov #{@WORD_NAME} #{newarg_ref}, #{value_ref}\n"
185
+ emit "mov #{@SCRATCH_REG}, #{value_ref}\n"
186
+ emit "mov #{newarg_ref}, #{@SCRATCH_REG}\n"
186
187
  end
187
188
  end
188
189
  end
189
190
 
190
191
  # Set register arguments
191
- if args.length > 0
192
- number_of_register_arguments(args.length).times do |i|
193
- register = @ARG_REGS[i]
194
- load_value_into_register args[i], register
195
- end
192
+ number_of_register_arguments(args.length).times do |i|
193
+ register = @ARG_REGS[i]
194
+ load_value_into_register args[i], register
196
195
  end
197
196
 
198
197
  # Tail call
@@ -219,8 +218,8 @@ module Voodoo
219
218
  "[rbp - #{(n + 1) * @WORDSIZE}]"
220
219
  else
221
220
  # Arguments that were originally passed on the stack
222
- # are now above rbp
223
- "[rbp + #{(n + 1 - number_of_register_arguments) * @WORDSIZE}]"
221
+ # are now above rbp, starting 2 words above it
222
+ "[rbp + #{(n + 2 - number_of_register_arguments) * @WORDSIZE}]"
224
223
  end
225
224
  end
226
225
 
@@ -10,6 +10,7 @@ module Voodoo
10
10
  # - #add_function
11
11
  # - #gensym
12
12
  # - #output_file_name
13
+ # - #output_file_suffix
13
14
  # - #wordsize
14
15
  # - #write
15
16
  #
@@ -41,6 +42,7 @@ module Voodoo
41
42
  self.section = :code
42
43
  @top_level = Environment.initial_environment
43
44
  @environment = @top_level
45
+ @output_file_suffix = '.o'
44
46
  end
45
47
 
46
48
  # Adds code to the given section.
@@ -81,9 +83,7 @@ module Voodoo
81
83
  keyword, args = action[0], action[1..-1]
82
84
  case keyword
83
85
  when :function
84
- begin_function *args[0]
85
- args[1..-1].each { |action| add section, action }
86
- end_function
86
+ function args[0], *args[1..-1]
87
87
  when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
88
88
  truebody = action[2]
89
89
  falsebody = action[3]
@@ -121,6 +121,13 @@ module Voodoo
121
121
  add :functions, [:function, formals] + code
122
122
  end
123
123
 
124
+ # Add a function to the current section
125
+ def function formals, *code
126
+ begin_function *formals
127
+ code.each { |action| add section, action }
128
+ end_function
129
+ end
130
+
124
131
  # Generate a new, unused symbol
125
132
  def gensym
126
133
  Environment.gensym
@@ -179,7 +186,12 @@ module Voodoo
179
186
  # Given an input file name, returns the canonical output file name
180
187
  # for this code generator.
181
188
  def output_file_name input_name
182
- input_name.sub(/\.voo$/, '') + '.o'
189
+ input_name.sub(/\.voo$/, '') + @output_file_suffix
190
+ end
191
+
192
+ # Returns the canonical output file suffix for this code generator
193
+ def output_file_suffix
194
+ @output_file_suffix
183
195
  end
184
196
 
185
197
  class Environment
@@ -0,0 +1,54 @@
1
+ require 'voodoo/config'
2
+ require 'voodoo/generators/command_postprocessor'
3
+
4
+ module Voodoo
5
+ # Class that generates ELF object files by invoking the GNU assembler on
6
+ # the output of a code generator that generates GNU assembly.
7
+ class GasELFGenerator
8
+ def initialize asmgenerator, extra_args
9
+ @asmgenerator = asmgenerator
10
+ @extra_args = extra_args
11
+ @output_file_suffix = '.o'
12
+ end
13
+
14
+ include CommandPostProcessor
15
+
16
+ def output_file_name input_name
17
+ input_name.sub(/\.voo$/, '') + @output_file_suffix
18
+ end
19
+
20
+ def output_file_suffix
21
+ @output_file_suffix
22
+ end
23
+
24
+ # Writes the generated code to the given IO handle
25
+ def write io
26
+ # Create temporary file to write assembly code to
27
+ if io.respond_to? :path
28
+ base = File.basename(io.path).sub(/([^.]*).*/, "\\1")
29
+ else
30
+ base = self.class.name
31
+ end
32
+
33
+ Tempfile.open(base + '.s') do |asmfile|
34
+ Tempfile.open(base + '.o') do |elffile|
35
+ elffile.close
36
+ # Write assembly code to nsamfile
37
+ @asmgenerator.write asmfile
38
+ asmfile.close
39
+ # Find out the name of the GNU assembler
40
+ gas = Voodoo::Config.gas_command
41
+ # Invoke gas on asmfile
42
+ command = "#{gas} #{@extra_args}" +
43
+ " -o #{shell_encode elffile.path}" +
44
+ " #{shell_encode asmfile.path}"
45
+ if system(command)
46
+ write_file_to_io elffile.path, io
47
+ end
48
+ elffile.unlink
49
+ end
50
+ asmfile.unlink
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,57 +1,27 @@
1
1
  require 'delegate'
2
2
  require 'tempfile'
3
- require 'voodoo/config'
4
3
  require 'voodoo/generators/i386_nasm_generator'
4
+ require 'voodoo/generators/nasm_elf_generator'
5
5
 
6
6
  module Voodoo
7
- # Generator that produces ELF objects
7
+ # Generator that produces ELF objects for i386
8
8
  class I386ELFGenerator < DelegateClass(I386NasmGenerator)
9
9
  def initialize params = {}
10
10
  @nasmgenerator = I386NasmGenerator.new params
11
11
  super(@nasmgenerator)
12
+ @elfgenerator = NasmELFGenerator.new @nasmgenerator, '-f elf32'
12
13
  end
13
14
 
14
15
  def output_file_name input_name
15
- input_name.sub(/\.voo$/, '') + '.o'
16
+ @elfgenerator.output_file_name input_name
16
17
  end
17
18
 
18
- # Encodes a string so that it is safe for use as a shell argument
19
- def shell_encode string
20
- '"' + string.gsub(/([\\`"$\n])/, "\\\\\\1") + '"'
19
+ def output_file_suffix
20
+ @elfgenerator.output_file_suffix
21
21
  end
22
22
 
23
- # Writes the generated code to the given IO handle
24
23
  def write io
25
- # Create temporary file to write NASM code to
26
- if io.respond_to? :path
27
- basename = File.basename(io.path).sub(/([^.]*).*/, "\\1")
28
- else
29
- basename = self.class.name
30
- end
31
- Tempfile.open(basename + '.asm') do |nasmfile|
32
- Tempfile.open(basename + '.o') do |elffile|
33
- elffile.close
34
- @nasmgenerator.write nasmfile
35
- nasmfile.close
36
- # Invoke nasm on nasmfile
37
- if ENV.has_key? 'NASM'
38
- nasm = ENV['NASM']
39
- elsif ENV.has_key? 'YASM'
40
- nasm = ENV['YASM']
41
- else
42
- nasm = Config.nasm_command
43
- end
44
- command = "#{nasm} -f elf -o #{shell_encode elffile.path}" +
45
- " #{shell_encode nasmfile.path}"
46
- if system(command)
47
- File.open(elffile.path) do |file|
48
- io.write file.read
49
- end
50
- end
51
- elffile.unlink
52
- end
53
- nasmfile.unlink
54
- end
24
+ @elfgenerator.write io
55
25
  end
56
26
  end
57
27
 
@@ -135,7 +135,7 @@ module Voodoo
135
135
 
136
136
  # Set arguments
137
137
  if args.length > 0
138
- (args.length - 1 .. 0).each do |i|
138
+ (args.length - 1).downto(0).each do |i|
139
139
  arg = args[i]
140
140
 
141
141
  value_ref = load_value arg, "eax"
@@ -0,0 +1,46 @@
1
+ require 'delegate'
2
+ require 'tempfile'
3
+ require 'voodoo/generators/mips_gas_generator'
4
+ require 'voodoo/generators/gas_elf_generator'
5
+
6
+ module Voodoo
7
+ # Generator that produces ELF objects for mips and mipsel
8
+ class MIPSELFGenerator < DelegateClass(MIPSGasGenerator)
9
+ def initialize params = {}
10
+ @asmgenerator = MIPSGasGenerator.new params
11
+ super(@asmgenerator)
12
+ case params[:architecture]
13
+ when :mips
14
+ byte_order = ' -EB'
15
+ when :mipsel
16
+ byte_order = ' -EL'
17
+ else
18
+ byte_order = ''
19
+ end
20
+ opts = '-KPIC' + byte_order
21
+ @elfgenerator = GasELFGenerator.new @asmgenerator, opts
22
+ end
23
+
24
+ def output_file_name input_name
25
+ @elfgenerator.output_file_name input_name
26
+ end
27
+
28
+ def output_file_suffix
29
+ @elfgenerator.output_file_suffix
30
+ end
31
+
32
+ def write io
33
+ @elfgenerator.write io
34
+ end
35
+ end
36
+
37
+ # Register class for mips
38
+ Voodoo::CodeGenerator.register_generator MIPSELFGenerator,
39
+ :architecture => :mips,
40
+ :format => :elf
41
+
42
+ # Register class for mipsel
43
+ Voodoo::CodeGenerator.register_generator MIPSELFGenerator,
44
+ :architecture => :mipsel,
45
+ :format => :elf
46
+ end
@@ -1,4 +1,4 @@
1
- require 'voodoo/generators/gas_generator'
1
+ require 'voodoo/generators/common_code_generator'
2
2
 
3
3
  module Voodoo
4
4
  # = MIPS GNU Assembler Code Generator
@@ -9,9 +9,19 @@ module Voodoo
9
9
  # == Calling Convention
10
10
  #
11
11
  # The first four arguments are passed in the registers $4 through $7.
12
- # Any additional arguments are passed in registers.
12
+ # Any additional arguments are passed on the stack, starting at
13
+ # $sp + 16. Words $sp through $sp + 12 will be available for the called
14
+ # function to use. $sp will always be a multiple of 8.
13
15
  #
14
- # The return value is passed in $2.
16
+ # The return address for the called function is passed in $31.
17
+ #
18
+ # When performing a position-independent call, the address of the called
19
+ # function is passed in $25.
20
+ #
21
+ # The called function will store its return value in $2.
22
+ #
23
+ # The called function is required to preserve the values of registers
24
+ # $16 through $23 and register $30.
15
25
  #
16
26
  # This calling convention is compatible with the System V ELF ABI.
17
27
  #
@@ -32,104 +42,592 @@ module Voodoo
32
42
  # empty1
33
43
  # empty0 <-- $sp points here
34
44
  #
35
- # Where $sp is $29, and must be a multiple of 8.
45
+ # The function prologue of functions generated by this code generator
46
+ # creates activiation frames that look as follows:
36
47
  #
37
- # TODO: Description of how we organize things below the original frame.
38
- # FIXME: Following is just a sketch and is likely neither complete nor
39
- # correct.
40
- # See the ELF ABI for MIPS, Standard Called Function Rules.
48
+ # :
49
+ # old frame
50
+ # argn
51
+ # :
52
+ # arg4
53
+ # arg3
54
+ # arg2
55
+ # arg1
56
+ # arg0
57
+ # return address
58
+ # pointer to Global Offset Table
59
+ # local0
60
+ # :
61
+ # local7
62
+ # local8
63
+ # :
64
+ # localn
65
+ # padding <-- $sp points here
41
66
  #
42
- # 1. Adjust $sp to create new stack frame
43
- # - Need space to save $sp, $r, and however many local variables
44
- # the function uses
45
- # - $sp must be a multiple of 8
46
- # 2. Calculate $gp (TODO: how?) and save it somewhere (either on the stack
47
- # or in a previously-saved callee-save register)
48
- # 3. Save $sp and $r
67
+ # In words:
49
68
  #
50
- # Note that, to comply with the standard called function rules laid out
51
- # in the System V ABI, a function can only have one exit. Thus, early
52
- # returns will have to be translated to gotos to the single exit.
69
+ # The four empty slots provided by the caller are used to store
70
+ # arguments 0 through 3 (originally passed in $4 through $7),
71
+ # if necessary.
53
72
  #
54
- # saved_r
55
- # saved_sp
56
- # :
57
- # local8
58
- # :
59
- # localn <-- $sp points here
73
+ # The stack frame created below the four slots provided by the caller
74
+ # contains the following data, in order:
75
+ #
76
+ # - Saved return address (originally passed in $31), if necessary.
60
77
  #
61
- # Register $0 is always 0.
78
+ # - Saved pointer to global offset table (computed from $25), if necessary.
62
79
  #
63
- # Registers $8 through $15 and register $24 are used as temporaries.
80
+ # - Saved values of caller's locals 0 through 7
81
+ # (originally in $16 through $23), if necessary.
64
82
  #
65
- # Registers $16 through $23 and register $30 are used for local
66
- # variables. They are callee-save, which means the value they had upon
67
- # function entry must be restored before returning.
83
+ # - Values of our locals > 8, if necessary.
68
84
  #
69
- # Register $25 is used to hold the address of the called function when
70
- # position-independent code is called. Otherwise, it may be used as
71
- # a temporary.
85
+ # - Padding to align $sp on a multiple of 8, if necessary.
72
86
  #
73
- # Registers $26 and $27 are reserved for operating system use.
74
87
  #
75
- # Register $28 is used as global pointer (FIXME: what does this mean?)
88
+ # In accordance with the System V ELF ABI for MIPS, the pointer to
89
+ # the global offset table is calculated as follows:
76
90
  #
77
- # Register $29 is used as stack pointer. It is callee-save: the
78
- # value it has upon function entry must be restored before returning.
79
- # The stack pointer must always be a multiple of 8.
91
+ # $gp = _gp_disp + $25
80
92
  #
81
- # Register $31 is used to store the return address when calling a function.
93
+ # where $gp is the register to store the pointer, $25 is the register
94
+ # holding the address of the called function, and _gp_disp is the
95
+ # offset between the beginning of the function and the global offset table.
82
96
  #
83
- module MIPSGasGenerator
97
+ class MIPSGasGenerator < CommonCodeGenerator
84
98
  def initialize params
85
99
  @WORDSIZE = 4
86
100
  @CODE_ALIGNMENT = 0
87
101
  @DATA_ALIGNMENT = @WORDSIZE
88
102
  @FUNCTION_ALIGNMENT = @WORDSIZE
103
+
104
+ @INITIAL_FRAME_SIZE = 2 * @WORDSIZE
105
+ @REGISTER_ARG_BASE = 4
106
+ @NREGISTER_ARGS = 4
107
+ @REGISTER_LOCAL_BASE = 16
108
+ @NREGISTER_LOCALS = 8
109
+ @RETURN = :'$2'
110
+ @TEMPORARY = :'$1'
111
+ @FUNCTION = :'$25'
112
+ @GOT = :'$28'
113
+ @TEMPORARIES = [:'$8', :'$9', :'$10', :'$11',
114
+ :'$12', :'$13', :'$14', :'$15']
115
+ @function_end_label = nil
116
+ @imports = {}
117
+ @if_labels = []
89
118
  super params
119
+ @output_file_suffix = '.s'
120
+ end
121
+
122
+ def align alignment = nil
123
+ unless alignment
124
+ # Get default alignment
125
+ case @section
126
+ when :code
127
+ alignment = @CODE_ALIGNMENT
128
+ when :data
129
+ alignment = @DATA_ALIGNMENT
130
+ when :function
131
+ alignment = @FUNCTION_ALIGNMENT
132
+ else
133
+ # Use data alignment as default
134
+ alignment = @DATA_ALIGNMENT
135
+ end
136
+ end
137
+ emit ".align #{alignment}\n" unless alignment == 0
138
+ end
139
+
140
+ # Return the offset from $sp at which the nth (0-based) argument is
141
+ # stored.
142
+ def arg_offset n
143
+ @frame_size + n * @WORDSIZE
90
144
  end
91
145
 
92
- #
93
- # == Functions
94
- #
146
+ # Return an $sp-relative reference for the nth (0-based) argument
147
+ def arg_reference n
148
+ "#{arg_offset n}($sp)"
149
+ end
150
+
151
+ # Return the register in which the nth (0-based) argument is stored, or
152
+ # nil if not stored in a register
153
+ def arg_register n
154
+ if register_arg? n
155
+ "$#{@REGISTER_ARG_BASE + n}"
156
+ else
157
+ nil
158
+ end
159
+ end
160
+
161
+ # Test if op is a binary operation
162
+ def assymetric_binop? op
163
+ [:div, :mod, :sub].member?(op)
164
+ end
95
165
 
96
166
  # Emit function prologue and declare _formals_ as function arguments
97
- def begin_function *formals
167
+ def begin_function formals, nlocals
168
+ if @environment != @top_level
169
+ raise "Cannot begin function when already in a function"
170
+ end
171
+
98
172
  emit "# function #{formals.join ' '}\n"
99
173
  environment = Environment.new @environment
100
174
  environment.add_args formals
101
175
  @environment = environment
102
- emit_function_prologue formals
176
+ @function_end_label = gensym
177
+ emit_function_prologue formals, nlocals
178
+ end
179
+
180
+ # Test if op is a binary operation
181
+ def binop? op
182
+ assymetric_binop?(op) || symmetric_binop?(op)
183
+ end
184
+
185
+ # Define a byte with the given value
186
+ def byte value
187
+ emit ".byte #{value}\n"
103
188
  end
104
189
 
105
190
  # Call a function.
106
191
  def call func, *args
107
- raise 'TODO'
192
+ emit "# call #{func} #{args.join ' '}\n"
193
+ # Grow the stack frame, subject to 3 conditions:
194
+ # 1. There must be enough space to hold all arguments
195
+ # 2. $sp must remain a multiple of 8
196
+ # 3. We must provide space for at least 4 words
197
+ increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
198
+ emit "addiu $sp, $sp, -#{increment}\n"
199
+ @frame_size = @frame_size + increment
200
+
201
+ # Put arguments in the right places
202
+ n = 0
203
+ while n < args.length
204
+ if n < @NREGISTER_ARGS
205
+ # Put arguments up to @NREGISTER_ARGS in registers
206
+ load_value_into_register args[n], arg_register(n)
207
+ else
208
+ # Put other arguments on the stack
209
+ load_value_into_register args[n], @TEMPORARY
210
+ emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
211
+ end
212
+ n = n + 1
213
+ end
214
+
215
+ # Load function address
216
+ if global? func
217
+ if @imports.has_key? func
218
+ # Load address from global offset table
219
+ emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
220
+ else
221
+ # Assume label defined in this module
222
+ emit "lui #{@FUNCTION}, %hi(#{func})\n"
223
+ emit "addiu #{@FUNCTION}, %lo(#{func})\n"
224
+ end
225
+ else
226
+ # Load address
227
+ load_value_into_register func, "#{@FUNCTION}"
228
+ end
229
+
230
+ # Perform call
231
+ emit "jalr #{@FUNCTION}\n"
232
+ # brach delay slot
233
+ emit "nop\n"
234
+
235
+ # Restore original stack frame
236
+ emit "addiu $sp, $sp, #{increment}\n"
237
+ @frame_size = @frame_size - increment
238
+
239
+ # restore gp
240
+ emit "lw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
108
241
  end
109
242
 
110
- # Emit function epilogue.
111
- def emit_function_epilogue formals = []
112
- raise 'TODO'
243
+ # Start a conditional using the specified branch instruction
244
+ # after the comparison.
245
+ def common_if comp, x, y = nil
246
+ emit "# #{comp} #{x} #{y}\n"
247
+
248
+ temporaries = @TEMPORARIES.dup
249
+ xreg = load_value x, temporaries[0]
250
+ temporaries.delete xreg
251
+ yreg = load_value y, temporaries[0]
252
+ temporaries.delete yreg
253
+
254
+ falselabel = @environment.gensym
255
+ @if_labels.push falselabel
256
+
257
+ case comp
258
+ when :ifeq
259
+ emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
260
+ when :ifge
261
+ emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
262
+ emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
263
+ when :ifgt
264
+ emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
265
+ emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
266
+ when :ifle
267
+ emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
268
+ emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
269
+ when :iflt
270
+ emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
271
+ emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
272
+ when :ifne
273
+ emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
274
+ else
275
+ raise "Unknown conditional: #{comp}"
276
+ end
277
+ emit "nop\n"
113
278
  end
114
279
 
115
280
  # Emit function prologue.
116
- def emit_function_prologue formals = []
117
- raise 'TODO'
281
+ def emit_function_prologue formals = [], nlocals = 0
282
+ # Calculate new value for $gp
283
+ emit "lui #{@GOT}, %hi(_gp_disp)\n"
284
+ emit "addiu #{@GOT}, %lo(_gp_disp)\n"
285
+ emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
286
+
287
+ # Save parameters 0 .. 3 in the stack space that the caller
288
+ # should have allocated for them.
289
+ [@NREGISTER_ARGS, formals.length].min.times do |n|
290
+ emit "sw $#{n + @REGISTER_ARG_BASE}, #{n * @WORDSIZE}($29)\n"
291
+ end
292
+
293
+ # Create initial frame
294
+ @frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
295
+ emit "addiu $sp, $sp, -#{@frame_size}\n"
296
+
297
+ # Save return address
298
+ emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
299
+
300
+ # Save gp
301
+ emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
118
302
  end
119
303
 
120
304
  # End a function body
121
305
  def end_function
122
- emit "# end function\n\n"
123
306
  if @environment == @top_level
124
307
  raise "Cannot end function when not in a function"
308
+ end
309
+
310
+ emit "# function epilogue\n"
311
+
312
+ label @function_end_label
313
+ # Restore saved locals
314
+ [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
315
+ emit "lw #{local_register n}, #{local_reference n}\n"
316
+ end
317
+ # Load return address
318
+ emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
319
+ # Restore stack pointer
320
+ emit "addiu $sp, $sp, #{@frame_size}\n"
321
+ # Return
322
+ emit "jr $31\n"
323
+ emit "nop\n"
324
+
325
+ emit "# end function\n\n"
326
+
327
+ @function_end_label = nil
328
+ @environment = @top_level
329
+ end
330
+
331
+ # End a conditional.
332
+ def end_if
333
+ label = @if_labels.pop
334
+ emit "#{label}:\n"
335
+ end
336
+
337
+ # Evaluate the binary operation expr and store the result in register
338
+ def eval_binop expr, register
339
+ temporaries = @TEMPORARIES.dup
340
+ x = load_value expr[1], temporaries[0]
341
+ temporaries.delete x
342
+ y = load_value expr[2], temporaries[0]
343
+ temporaries.delete y
344
+
345
+ case expr[0]
346
+ when :div
347
+ emit "div $0, #{x}, #{y}\n"
348
+ emit "mflo #{register}\n"
349
+ when :mod
350
+ emit "div $0, #{x}, #{y}\n"
351
+ emit "mfhi #{register}\n"
352
+ when :mul
353
+ emit "mult #{x}, #{y}\n"
354
+ emit "mflo #{register}\n"
125
355
  else
126
- @environment = @top_level
356
+ emit "#{expr[0]} #{register}, #{x}, #{y}\n"
127
357
  end
128
358
  end
129
359
 
130
- # Call a function, re-using the current call frame if possible.
131
- def tail_call func, *args
132
- raise 'TODO'
360
+ # Evaluate the expression expr and store the result in register
361
+ def eval_expr expr, register
362
+ if expr.length == 1
363
+ # Load value
364
+ load_value_into_register expr[0], register
365
+ else
366
+ # Evaluate expression
367
+ op = expr[0]
368
+ case op
369
+ when :call
370
+ call *expr[1..-1]
371
+ emit "move #{register}, #{@RETURN}\n" if register != @RETURN
372
+ when :'get-byte'
373
+ get_byte expr[1], expr[2], register
374
+ when :'get-word'
375
+ get_word expr[1], expr[2], register
376
+ when :not
377
+ eval_binop [:nor, 0, expr[1]], register
378
+ else
379
+ if binop? op
380
+ eval_binop expr, register
381
+ else
382
+ raise "Not a magic word: #{op}"
383
+ end
384
+ end
385
+ end
386
+ end
387
+
388
+ # Export symbols from the current section
389
+ def export *symbols
390
+ symbols.each { |sym| emit ".globl #{sym}\n" }
391
+ end
392
+
393
+ # Add a function to the current section
394
+ def function formals, *code
395
+ # Set nlocals to the number of local variables in the function
396
+ nlocals = 0
397
+ code.each { |action| nlocals = nlocals + 1 if action[0] == :let }
398
+ begin_function formals, nlocals
399
+ code.each { |action| add section, action }
400
+ end_function
401
+ end
402
+
403
+ # Load byte from _base_ + _offset_ into _register_
404
+ def get_byte base, offset, register
405
+ # If base is an integer, but offset isn't, swap them
406
+ if !integer?(offset) && integer?(base)
407
+ base, offset = [offset, base]
408
+ end
409
+
410
+ if integer? offset
411
+ base_reg = load_value base
412
+ emit "lb #{register}, #{offset}(#{base_reg})\n"
413
+ else
414
+ eval_binop [:add, base, offset], @TEMPORARY
415
+ emit "lb #{register}, 0(#{@TEMPORARY})\n"
416
+ end
417
+ end
418
+
419
+ # Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
420
+ def get_word base, offset, register
421
+ if integer? offset
422
+ base_reg = load_value base
423
+ emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
424
+ else
425
+ offset_reg = @TEMPORARIES.pop
426
+ begin
427
+ load_value_into_register offset, offset_reg
428
+ base_reg = load_value base
429
+ emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
430
+ emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
431
+ emit "lw #{register}, 0(#{@TEMPORARY})\n"
432
+ ensure
433
+ @TEMPORARIES.push offset_reg
434
+ end
435
+ end
436
+ end
437
+
438
+ # Test if a symbol refers to a global
439
+ def global? symbol
440
+ symbol?(symbol) && @environment[symbol] == nil
441
+ end
442
+
443
+ # Jump to a label.
444
+ def goto label
445
+ emit "j #{label}\n"
446
+ emit "nop\n"
447
+ end
448
+
449
+ # Start the false path of a conditional.
450
+ def ifelse
451
+ emit "# else\n"
452
+ newlabel = @environment.gensym
453
+ emit "j #{newlabel}\n"
454
+ emit "nop\n"
455
+ label = @if_labels.pop
456
+ emit "#{label}:\n"
457
+ @if_labels.push newlabel
458
+ end
459
+
460
+ # Test if x is equal to y
461
+ def ifeq x, y
462
+ common_if :ifeq, x, y
463
+ end
464
+
465
+ # Test if x is greater than or equal to y
466
+ def ifge x, y
467
+ common_if :ifge, x, y
468
+ end
469
+
470
+ # Test if x is strictly greater than y
471
+ def ifgt x, y
472
+ common_if :ifgt, x, y
473
+ end
474
+
475
+ # Test if x is less than or equal to y
476
+ def ifle x, y
477
+ common_if :ifle, x, y
478
+ end
479
+
480
+ # Test if x is strictly less than y
481
+ def iflt x, y
482
+ common_if :iflt, x, y
483
+ end
484
+
485
+ # Test if x different from y
486
+ def ifne x, y
487
+ common_if :ifne, x, y
488
+ end
489
+
490
+ # Import labels into the current section
491
+ def import *symbols
492
+ # Record imported labels in @imports
493
+ symbols.each { |sym| @imports[sym] = sym }
494
+ end
495
+
496
+ # Test if a value is an integer
497
+ def integer? value
498
+ value.kind_of? Integer
499
+ end
500
+
501
+ # Emit a label
502
+ def label name
503
+ emit "#{name}:\n"
504
+ end
505
+
506
+ # Introduce a new local variable
507
+ def let symbol, *expr
508
+ emit "# let #{symbol} #{expr.join ' '}\n"
509
+ n = @environment.locals
510
+ @environment.add_local symbol
511
+
512
+ # Extend stack frame, if necessary
513
+ if @environment.locals > max_locals
514
+ $stderr.puts "WARNING: Stack frame too small. Possible BUG"
515
+ # Extend frame by 8 bytes, to keep $sp a multiple of 8
516
+ emit "addiu $sp, $sp, -8\n"
517
+ @frame_size = @frame_size + 8
518
+ end
519
+
520
+ register = local_register n
521
+ ref = local_reference n
522
+ if register
523
+ # Save current value of register
524
+ emit "sw #{register}, #{ref}\n"
525
+ # Set new value
526
+ eval_expr expr, register
527
+ else
528
+ eval_expr expr, @TEMPORARY
529
+ emit "sw #{@TEMPORARY}, #{ref}\n"
530
+ end
531
+ end
532
+
533
+ # Load a value into a register.
534
+ # Returns the name of the register.
535
+ # If the value was already in a register, the name of that
536
+ # register is returned.
537
+ # Else, the value is loaded into a register and the name of
538
+ # that register is returned. The register to use in that case
539
+ # may be specified using the optional second argument.
540
+ def load_value x, register = @TEMPORARY
541
+ if x == 0
542
+ return "$0"
543
+ elsif integer? x
544
+ if x >= -32768 && x <= 32767
545
+ emit "addiu #{register}, $0, #{x}\n"
546
+ return register
547
+ else
548
+ emit "lui #{register}, #{x >> 16}\n"
549
+ emit "ori #{register}, #{register}, #{x & 0xffff}\n"
550
+ return register
551
+ end
552
+ elsif symbol? x
553
+ binding = @environment[x]
554
+ if binding
555
+ case binding[0]
556
+ when :arg
557
+ emit "lw #{register}, #{arg_reference binding[1]}\n"
558
+ return register
559
+ when :local
560
+ n = binding[1]
561
+ if register_local? n
562
+ return local_register(n)
563
+ else
564
+ emit "lw #{register}, #{local_reference n}\n"
565
+ return register
566
+ end
567
+ else
568
+ raise "Don't know how to load #{x.inspect}"
569
+ end
570
+ else
571
+ # Assume global
572
+ emit "lui #{register}, %hi(#{x})\n"
573
+ emit "addiu #{register}, %lo(#{x})\n"
574
+ return register
575
+ end
576
+ else
577
+ raise "Don't know how to load #{x.inspect}"
578
+ end
579
+ end
580
+
581
+ # Load a value into a specific register
582
+ def load_value_into_register x, register
583
+ reg = load_value x, register
584
+ if reg != register
585
+ emit "move #{register}, #{reg}\n"
586
+ end
587
+ end
588
+
589
+ # Return the offset from $sp at which the nth (0-based) local variable is
590
+ # stored. For register locals this is the offset at which the saved
591
+ # value (from the calling function) is stored.
592
+ def local_offset n
593
+ @frame_size - @INITIAL_FRAME_SIZE - (n + 1) * @WORDSIZE
594
+ end
595
+
596
+ # Return an $sp-relative reference for the nth (0-based) local
597
+ def local_reference n
598
+ "#{local_offset n}($sp)"
599
+ end
600
+
601
+ # Return the register in which the nth local (0-based) is stored, or
602
+ # nil if not stored in a register
603
+ def local_register n
604
+ if register_local? n
605
+ "$#{@REGISTER_LOCAL_BASE + n}"
606
+ else
607
+ nil
608
+ end
609
+ end
610
+
611
+ # Compute the maximum number of locals that would fit in the current
612
+ # frame size.
613
+ def max_locals
614
+ (@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE
615
+ end
616
+
617
+ # Calculate the number of stack arguments,
618
+ # given the total number of arguments.
619
+ def number_of_stack_arguments n
620
+ [0, n - @NREGISTER_ARGS].max
621
+ end
622
+
623
+ # Returns true if the nth (0-based) argument is stored in a register
624
+ def register_arg? n
625
+ n < @NREGISTER_ARGS
626
+ end
627
+
628
+ # Returns true if the nth (0-based) local is stored in a register
629
+ def register_local? n
630
+ n < @NREGISTER_LOCALS
133
631
  end
134
632
 
135
633
  # Return a from a function.
@@ -138,11 +636,251 @@ module Voodoo
138
636
  # of the evaluation is returned from the function.
139
637
  def ret *words
140
638
  emit "# return #{words.join ' '}\n"
141
- eval_expr words
142
- emit_function_epilogue
143
- emit "j $31\n"
639
+ # Compute return value and store it in @RETURN
640
+ eval_expr words, @RETURN
641
+ # Go to epilogue
642
+ goto @function_end_label
144
643
  end
145
-
146
644
 
645
+ # Set a variable to the result of evaluating an expression
646
+ def set symbol, *expr
647
+ emit "# set #{symbol} #{expr.join ' '}\n"
648
+
649
+ x = @environment[symbol]
650
+ if x == nil
651
+ raise "Cannot change value of constant #{symbol}"
652
+ end
653
+
654
+ if x[0] == :local
655
+ register = local_register x[1]
656
+ else
657
+ register = nil
658
+ end
659
+
660
+ if register
661
+ # Set new value
662
+ eval_expr expr, register
663
+ else
664
+ case x[0]
665
+ when :local
666
+ ref = local_reference x[1]
667
+ when :arg
668
+ ref = arg_reference x[1]
669
+ else
670
+ raise "??? #{sym} is neither a local nor an argument"
671
+ end
672
+ eval_expr expr, @TEMPORARY
673
+ emit "sw #{@TEMPORARY}, #{ref}\n"
674
+ end
675
+ end
676
+
677
+ # Set the byte at _base_ + _offset_ to _value_
678
+ def set_byte base, offset, value
679
+ emit "# set-byte #{base} #{offset} #{value}\n"
680
+ # If base is an integer, but offset isn't, swap them
681
+ if !integer?(offset) && integer?(base)
682
+ base, offset = [offset, base]
683
+ end
684
+
685
+ value_reg = @TEMPORARIES.pop
686
+ load_value_into_register value, value_reg
687
+ begin
688
+ if integer? offset
689
+ base_reg = load_value base
690
+ emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
691
+ else
692
+ eval_binop [:add, base, offset], @TEMPORARY
693
+ emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
694
+ end
695
+ ensure
696
+ @TEMPORARIES.push value_reg
697
+ end
698
+ end
699
+
700
+ # Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
701
+ def set_word base, offset, value
702
+ emit "# set-word #{base} #{offset} #{value}\n"
703
+
704
+ value_reg = @TEMPORARIES.pop
705
+ load_value_into_register value, value_reg
706
+ begin
707
+ if integer? offset
708
+ base_reg = load_value base
709
+ emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
710
+ else
711
+ offset_reg = @TEMPORARIES.pop
712
+ begin
713
+ load_value_into_register offset, offset_reg
714
+ base_reg = load_value base
715
+ emit "sll #{offset_reg}, #{offset_reg}, 2\n"
716
+ emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
717
+ emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
718
+ ensure
719
+ @TEMPORARIES.push offset_reg
720
+ end
721
+ end
722
+ ensure
723
+ @TEMPORARIES.push value_reg
724
+ end
725
+ end
726
+
727
+ # Define a string with the given value
728
+ def string value
729
+ code = ''
730
+ value.each_byte do |b|
731
+ if b >= 32 && b < 128
732
+ code << b.chr
733
+ else
734
+ code << sprintf("\\%03o", b)
735
+ end
736
+ end
737
+ emit ".ascii \"#{code}\"\n"
738
+ end
739
+
740
+ # Test if a value is a symbol
741
+ def symbol? value
742
+ value.kind_of? Symbol
743
+ end
744
+
745
+ # Test if op is a symmetric binary operation (i.e. it will yield the
746
+ # same result if the order of its source operands is changed).
747
+ def symmetric_binop? op
748
+ [:add, :and, :mul, :or, :xor].member? op
749
+ end
750
+
751
+ # Call a function, re-using the current call frame if possible.
752
+ def tail_call func, *args
753
+ emit "# tail-call #{func} #{args.join ' '}\n"
754
+
755
+ # Compute number of stack arguments
756
+ nstackargs = number_of_stack_arguments args.length
757
+ # If we need more stack arguments than we have now,
758
+ # perform a normal call and return
759
+ if nstackargs > number_of_stack_arguments(@environment.args)
760
+ emit "# Not enough space for proper tail call; using regular call\n"
761
+ ret :call, func, *args
762
+ end
763
+
764
+ # TODO
765
+
766
+ # Back up any stack arguments we will need after we overwrite them
767
+ temporaries = @TEMPORARIES.dup
768
+ nlocals = @environment.locals
769
+ (@NREGISTER_ARGS...args.length).each do |i|
770
+ x = @environment[args[i]]
771
+ if x && x[0] == :arg && x[1] >= @NREGISTER_ARGS && x[1] < i
772
+ # args[i] is a stack arg, but has an index < i
773
+ # That means that, by the time we get to pass arg[i] to the called
774
+ # function, it will have been overwritten. So we make a backup.
775
+ if temporaries.empty?
776
+ # Oh dear, we're out of temporaries.
777
+ # Store the value in the current stack frame, extending it
778
+ # as necessary.
779
+ if nlocals >= max_locals
780
+ emit "addiu $sp, -8\n"
781
+ @frame_size = @frame_size + 8
782
+ end
783
+ reg = load_value args[i]
784
+ emit "sw #{reg}, #{local_reference nlocals}\n"
785
+ args[i] = [:local, nlocals]
786
+ nlocals = nlocals + 1
787
+ else
788
+ # Load the argument into a temporary
789
+ reg = temporaries.shift
790
+ load_value_into_register args[i], reg
791
+ # Update args[i] so we know how to get to it later
792
+ args[i] = [:reg, reg]
793
+ end
794
+ end
795
+ end
796
+
797
+ # Set stack arguments
798
+ (@NREGISTER_ARGS...args.length).each do |i|
799
+ arg = args[i]
800
+ reg = @TEMPORARY
801
+
802
+ # Load value
803
+ if arg.kind_of? Array
804
+ # Special cases, created above
805
+ case arg[0]
806
+ when :reg
807
+ reg = arg[1]
808
+ when :local
809
+ emit "lw #{reg}, #{local_reference arg[1]}\n"
810
+ end
811
+ else
812
+ load_value_into_register arg, reg
813
+ end
814
+
815
+ # Store value in designated place
816
+ emit "sw #{reg}, #{arg_reference i}\n"
817
+ end
818
+
819
+ # Set register arguments
820
+ [@NREGISTER_ARGS,args.length].min.times do |i|
821
+ reg = arg_register i
822
+ load_value_into_register args[i], reg
823
+ end
824
+
825
+ # Restore saved registers
826
+ [@NREGISTER_LOCALS, @environment.locals].min.times do |n|
827
+ emit "lw #{local_register n}, #{local_reference n}\n"
828
+ end
829
+ # Restore return address
830
+ emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
831
+
832
+ # Adjust stack pointer
833
+ emit "addiu $sp, #{@frame_size}\n"
834
+
835
+ # Load function address
836
+ if global? func
837
+ if @imports.has_key? func
838
+ # Load address from global offset table
839
+ emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
840
+ else
841
+ # Assume label defined in this module
842
+ emit "lui #{@FUNCTION}, %hi(#{func})\n"
843
+ emit "addiu #{@FUNCTION}, %lo(#{func})\n"
844
+ end
845
+ else
846
+ # Load address
847
+ load_value_into_register func, "#{@FUNCTION}"
848
+ end
849
+
850
+ # Perform call
851
+ emit "jr #{@FUNCTION}\n"
852
+ # brach delay slot
853
+ emit "nop\n"
854
+ end
855
+
856
+ # Define a word with the given value
857
+ def word value
858
+ emit ".int #{value}\n"
859
+ end
860
+
861
+ # Write generated code to the given IO object.
862
+ def write io
863
+ io.puts ".set noat"
864
+ @sections.each do |section,code|
865
+ unless code.empty?
866
+ io.puts ".section #{section.to_s}"
867
+ io.puts code
868
+ io.puts
869
+ end
870
+ end
871
+ end
872
+
147
873
  end
874
+
875
+ # Register class for big endian MIPS
876
+ Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
877
+ :architecture => :mips,
878
+ :format => :gas
879
+
880
+
881
+ # Register class for little endian MIPS
882
+ Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
883
+ :architecture => :mipsel,
884
+ :format => :gas
885
+
148
886
  end