voodoo 0.5.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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