voodoo 1.0.2 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,9 +3,10 @@
3
3
  require 'getoptlong'
4
4
 
5
5
  require 'voodoo'
6
+ require 'voodoo/generators/dummy_generator'
6
7
 
7
- usage="USAGE: voodooc [options] <input file>"
8
- help=<<EOT
8
+ USAGE="USAGE: voodooc [options] <input file>"
9
+ HELP=<<EOT
9
10
  Input file denotes the file to compile. The special name '-' causes
10
11
  voodooc to read from standard input. In that case, the -o option is
11
12
  mandatory.
@@ -18,6 +19,10 @@ Valid options are:
18
19
  Select target architecture. Use -a help to get a list of
19
20
  supported architectures.
20
21
 
22
+ -c
23
+ --check
24
+ Check program for correctness, but do not actually compile it.
25
+
21
26
  -f <format>
22
27
  --format <format>
23
28
  --output-format <format>
@@ -25,7 +30,7 @@ Valid options are:
25
30
  supported output formats for architecture.
26
31
 
27
32
  --features
28
- Lists the features supported by this implementation. The features
33
+ List the features supported by this implementation. The features
29
34
  are listed in the format <feature name><tab><version>, ordered
30
35
  by feature name. The program exits after printing the list of
31
36
  features; no compilation is performed.
@@ -44,21 +49,67 @@ Valid options are:
44
49
  Display version information and exit. No compilation will be performed.
45
50
  EOT
46
51
 
52
+ architecture = Voodoo::Config.default_architecture
53
+ check_only = false
47
54
  input_file = nil
48
55
  output_file = nil
49
- architecture = Voodoo::Config.default_architecture
50
56
  output_format = Voodoo::Config.default_format
51
57
  show_features = false
52
58
 
59
+ # Gets the input file name from the command line.
60
+ def get_input_file_name
61
+ if ARGV.length == 1
62
+ input_file = ARGV[0]
63
+ else
64
+ if ARGV.length < 1
65
+ $stderr.puts "no input files"
66
+ else
67
+ $stderr.puts "Too many arguments"
68
+ end
69
+ $stderr.puts USAGE
70
+ exit 0x80
71
+ end
72
+ input_file
73
+ end
74
+
75
+ # If error is a Voodoo::Compiler::Error, prints error messages.
76
+ # Else, re-raises error.
77
+ def handle_error error
78
+ # If error is a compiler error, iterate over all child errors and
79
+ # print user-friendly messages. Else, re-raise so that the default
80
+ # handling is performed.
81
+ if error.kind_of? Voodoo::Compiler::Error
82
+ error.errors.each do |e|
83
+ message = ''
84
+ if e.start_line != nil
85
+ message << "#{e.start_line}: "
86
+ if e.input_name != nil
87
+ message = "#{e.input_name}:#{message}"
88
+ else
89
+ message = "line #{message}"
90
+ end
91
+ end
92
+ message << e.message
93
+ $stderr.puts message
94
+ if e.text != nil
95
+ $stderr.print "\n #{e.text.gsub("\n", "\n ")}\n"
96
+ end
97
+ end
98
+ else
99
+ raise error
100
+ end
101
+ end
102
+
53
103
  #
54
104
  # Process command line
55
105
  #
56
106
 
57
107
  opts = GetoptLong.new(
58
- ['--help', '-h', GetoptLong::NO_ARGUMENT ],
59
- ['--arch', '--architecture', '-a', GetoptLong::REQUIRED_ARGUMENT ],
108
+ ['--arch', '--architecture', '-a', GetoptLong::REQUIRED_ARGUMENT],
109
+ ['--check', '-c', GetoptLong::NO_ARGUMENT],
60
110
  ['--features', GetoptLong::NO_ARGUMENT],
61
- ['--output-file', '--output', '-o', GetoptLong::REQUIRED_ARGUMENT ],
111
+ ['--help', '-h', GetoptLong::NO_ARGUMENT],
112
+ ['--output-file', '--output', '-o', GetoptLong::REQUIRED_ARGUMENT],
62
113
  ['--output-format', '--format', '-f', GetoptLong::REQUIRED_ARGUMENT ],
63
114
  ['--version', GetoptLong::NO_ARGUMENT]
64
115
  )
@@ -66,14 +117,16 @@ opts = GetoptLong.new(
66
117
  # Process options
67
118
  opts.each do |opt, arg|
68
119
  case opt
69
- when '--help'
70
- puts usage
71
- puts help
72
- exit
73
120
  when '--arch'
74
121
  architecture = arg.to_sym
122
+ when '--check'
123
+ check_only = true
75
124
  when '--features'
76
125
  show_features = true
126
+ when '--help'
127
+ puts USAGE
128
+ puts HELP
129
+ exit
77
130
  when '--output-file'
78
131
  output_file = arg
79
132
  when '--output-format'
@@ -99,6 +152,24 @@ if output_format == :help
99
152
  exit
100
153
  end
101
154
 
155
+ if check_only
156
+ input_file = get_input_file_name
157
+ infile = input_file == '-' ? $stdin : open(input_file)
158
+ begin
159
+ parser = Voodoo::Parser.new infile
160
+ generator = Voodoo::DummyGenerator.new
161
+ compiler = Voodoo::Compiler.new parser, generator, nil
162
+ compiler.compile
163
+ rescue => error
164
+ handle_error error
165
+ exit 1
166
+ ensure
167
+ infile.close
168
+ end
169
+ puts 'OK'
170
+ exit 0
171
+ end
172
+
102
173
  # Select code generator based on output format
103
174
  begin
104
175
  generator = Voodoo::CodeGenerator.get_generator :architecture => architecture,
@@ -124,20 +195,7 @@ if show_features
124
195
  exit
125
196
  end
126
197
 
127
- # Get input file name
128
- if ARGV.length == 1
129
- input_file = ARGV[0]
130
- else
131
- if ARGV.length < 1
132
- $stderr.puts "no input files"
133
- else
134
- $stderr.puts "Too many arguments"
135
- end
136
- $stderr.puts usage
137
- exit 0x80
138
- end
139
-
140
-
198
+ input_file = get_input_file_name
141
199
 
142
200
  #
143
201
  # Compile
@@ -171,30 +229,8 @@ rescue => e
171
229
  File::unlink(output_file) if File::exists?(output_file)
172
230
  end
173
231
 
174
- # If e is a compiler error, iterate over all child errors and
175
- # print user-friendly messages. Else, re-raise so that the default
176
- # handling is performed.
177
- if e.kind_of? Voodoo::Compiler::Error
178
- e.errors.each do |error|
179
- message = ''
180
- if error.start_line != nil
181
- message << "#{error.start_line}: "
182
- if error.input_name != nil
183
- message = "#{error.input_name}:#{message}"
184
- else
185
- message = "line #{message}"
186
- end
187
- end
188
- message << error.message
189
- $stderr.puts message
190
- if error.text != nil
191
- $stderr.print "\n #{error.text.gsub("\n", "\n ")}\n"
192
- end
193
- end
194
- exit 1
195
- else
196
- raise
197
- end
232
+ handle_error e
233
+ exit 1
198
234
  ensure
199
235
  outfile.close
200
236
  infile.close
@@ -10,22 +10,27 @@ require 'voodoo/parser'
10
10
  # programming language designed to be a thin abstraction of the CPU's
11
11
  # native instruction set.
12
12
  #
13
- # The compiler consists of three parts:
13
+ # The compiler consists of four parts:
14
14
  #
15
15
  # 1. The parser (Voodoo::Parser), which reads
16
16
  # Voodoo[http://inglorion.net/documents/designs/voodoo/] source code and
17
17
  # turns it into Ruby[http://www.ruby-lang.org/] objects.
18
18
  #
19
- # 2. The code generator (a class that implements the methods of
19
+ # 2. The validator (Voodoo::Validator), which takes Voodoo code in Ruby
20
+ # object form and checks that it is valid. The parser calls the validator,
21
+ # so if you are using the parser to get Voodoo code, it will already have
22
+ # been validated.
23
+ #
24
+ # 3. The code generator (a class that implements the methods of
20
25
  # Voodoo::CommonCodeGenerator), which provides methods that generate
21
26
  # code for the target platform.
22
27
  #
23
- # 3. The compiler driver (Voodoo::Compiler), which reads from the parser
28
+ # 4. The compiler driver (Voodoo::Compiler), which reads from the parser
24
29
  # and calls the appropriate methods of the code generator.
25
30
  #
26
- # Both the parser and the code generators can be used on their own. For
27
- # example, you could use the code generator to generate native code for
28
- # your own programming language without first creating a
31
+ # The parser, validator, and code generators can be used on their own.
32
+ # For example, you could use the code generator to generate native
33
+ # code for your own programming language without first creating a
29
34
  # Voodoo[http://inglorion.net/documents/designs/voodoo/] program.
30
35
  #
31
36
  # Instead of instantiating a code generator directly, it is recommended
@@ -11,7 +11,7 @@ module Voodoo
11
11
 
12
12
  module_function
13
13
 
14
- # Register a code generator.
14
+ # Registers a code generator.
15
15
  # Example:
16
16
  # Voodoo::CodeGenerator.register_generator I386NasmGenerator,
17
17
  # :architecture => :i386,
@@ -28,7 +28,7 @@ module Voodoo
28
28
  end
29
29
  end
30
30
 
31
- # Get a code generator for the specified parameters
31
+ # Gets a code generator for the specified parameters.
32
32
  def get_generator params = {}
33
33
  params[:architecture] = Config.default_architecture \
34
34
  unless params[:architecture]
@@ -44,23 +44,23 @@ module Voodoo
44
44
  end
45
45
  end
46
46
 
47
- # Tests if a given architecture is supported
47
+ # Tests if a given architecture is supported.
48
48
  def architecture_supported? arch
49
49
  @@generators.has_key? arch.to_sym
50
50
  end
51
51
 
52
- # Get an array of supported architectures
52
+ # Gets an array of supported architectures.
53
53
  def architectures
54
54
  @@generators.keys
55
55
  end
56
56
 
57
- # Tests if a given format is supported for a given architecture
57
+ # Tests if a given format is supported for a given architecture.
58
58
  def format_supported? arch, format
59
59
  architecture_supported?(arch.to_sym) &&
60
60
  @@generators[arch.to_sym].has_key?(format.to_sym)
61
61
  end
62
62
 
63
- # Get an array of supported formats for a given architecture
63
+ # Gets an array of supported formats for a given architecture.
64
64
  def formats architecture
65
65
  @@generators[architecture.to_sym].keys
66
66
  end
@@ -23,7 +23,7 @@ module Voodoo
23
23
  end
24
24
  end
25
25
 
26
- # Initialize a compiler.
26
+ # Initializes a compiler.
27
27
  #
28
28
  # Parameters:
29
29
  # [parser] the parser to be used (see Voodoo::Parser)
@@ -36,7 +36,7 @@ module Voodoo
36
36
  @output = output
37
37
  end
38
38
 
39
- # Perform the compilation.
39
+ # Performs the compilation.
40
40
  def compile
41
41
  section = :code
42
42
  errors = []
@@ -2,15 +2,15 @@ module Voodoo
2
2
  # Methods to get and set configuration parameters
3
3
  module Config
4
4
  IMPLEMENTATION_NAME = 'Voodoo Compiler'
5
- IMPLEMENTATION_VERSION = '1.0.2'
5
+ IMPLEMENTATION_VERSION = '1.1.1'
6
6
 
7
7
  # Class that holds configuration parameters
8
8
  class Configuration
9
9
  def initialize
10
10
  @default_architecture = :auto
11
11
  @default_format = :elf
12
- @gas_command = "as"
13
- @nasm_command = "nasm"
12
+ @gas_command = "/usr/bin/as"
13
+ @nasm_command = ""
14
14
  end
15
15
 
16
16
  attr_accessor :default_format,
@@ -1,4 +1,5 @@
1
1
  require 'voodoo/generators/nasm_generator'
2
+ require 'set'
2
3
 
3
4
  module Voodoo
4
5
  # = AMD64 NASM Code Generator
@@ -45,15 +46,25 @@ module Voodoo
45
46
  # arg_1
46
47
  # :
47
48
  # arg_5
48
- # local_0
49
- # local_1
49
+ # saved_r12
50
+ # :
51
+ # saved_r15
52
+ # local_4
50
53
  # :
51
54
  # local_n <-- rsp
52
55
  #
56
+ #
57
+ # == Callee-Save Registers
58
+ #
59
+ # +rbp+, +rbx+, and +r12+ through +r15+ are callee-save registers.
60
+ #
61
+ # All other registers are caller-save.
62
+ #
53
63
  class AMD64NasmGenerator < NasmGenerator
54
64
  def initialize params = {}
55
65
  # Number of bytes in a word
56
- @WORDSIZE = 8
66
+ @WORDSIZE_BITS = 3
67
+ @WORDSIZE = 1 << @WORDSIZE_BITS
57
68
  # Word name in NASM lingo
58
69
  @WORD_NAME = 'qword'
59
70
  # Default alignment for code
@@ -63,28 +74,40 @@ module Voodoo
63
74
  # Default alignment for functions
64
75
  @FUNCTION_ALIGNMENT = 16
65
76
  # Register used for return values
66
- @RETURN_REG = 'rax'
67
- # Register used as scratch register
68
- @SCRATCH_REG = 'r11'
77
+ @RETURN_REG = :rax
78
+ # Stack alignment restrictions
79
+ @STACK_ALIGNMENT_BITS = @WORDSIZE_BITS
80
+ @STACK_ALIGNMENT = 1 << @STACK_ALIGNMENT_BITS
81
+ @TEMPORARIES = [:r11]
69
82
  # Registers used for argument passing
70
- @ARG_REGS = ['rdi', 'rsi', 'rdx', 'rcx', 'r8', 'r9']
83
+ @ARG_REGS = [:rdi, :rsi, :rdx, :rcx, :r8, :r9]
84
+ @NREG_ARGS = @ARG_REGS.length
85
+ # Registers used to store locals
86
+ @LOCAL_REGISTERS = [:r12, :r13, :r14, :r15]
87
+ @NLOCAL_REGISTERS = @LOCAL_REGISTERS.length
88
+ @LOCAL_REGISTERS_SET = Set.new @LOCAL_REGISTERS
71
89
  # Accumulator index
72
- @AX = 'rax'
90
+ @AX = :rax
73
91
  # Base index
74
- @BX = 'rbx'
92
+ @BX = :rbx
75
93
  # Count index
76
- @CX = 'rcx'
94
+ @CX = :rcx
77
95
  # Data index
78
- @DX = 'rdx'
96
+ @DX = :rdx
79
97
  # Base pointer
80
- @BP = 'rbp'
98
+ @BP = :rbp
81
99
  # Stack pointer
82
- @SP = 'rsp'
100
+ @SP = :rsp
101
+ @SAVE_FRAME_REGISTERS = [:rbx, :r12, :r13, :r14, :r15, :rsp, :rbp]
102
+ @SAVED_FRAME_LAYOUT = {}
103
+ @SAVE_FRAME_REGISTERS.each_with_index { |r,i| @SAVED_FRAME_LAYOUT[r] = i }
83
104
  super params
84
105
  @features.merge! \
85
106
  :'bits-per-word' => '64',
86
107
  :'byte-order' => 'little-endian',
87
108
  :'bytes-per-word' => '8'
109
+ @saved_registers = []
110
+ @function_end_label = nil
88
111
  end
89
112
 
90
113
  #
@@ -100,15 +123,33 @@ module Voodoo
100
123
  # == Functions
101
124
  #
102
125
 
103
- # Call a function.
126
+ # Emits function preamble and declare +formals+ as function arguments.
127
+ def begin_function formals, nlocals
128
+ environment = Environment.new @environment
129
+ @saved_registers = []
130
+ @environment = environment
131
+ emit "push #{@BP}\nmov #{@BP}, #{@SP}\n"
132
+ formals.each_with_index do |arg,i|
133
+ @environment.add_arg arg, arg_offset(i)
134
+ comment "# #{arg} is at #{offset_reference(@environment[arg])}"
135
+ emit "push #{@ARG_REGS[i]}\n" if i < @NREG_ARGS
136
+ end
137
+ emit "sub #{@SP}, #{nlocals * @WORDSIZE}\n"
138
+ number_of_register_locals(nlocals).times do |i|
139
+ register = @LOCAL_REGISTERS[i]
140
+ ref = offset_reference saved_local_offset(i)
141
+ emit "mov #{ref}, #{register}\n"
142
+ @saved_registers << register
143
+ end
144
+ @function_end_label = gensym
145
+ end
146
+
147
+ # Calls a function.
104
148
  def call func, *args
105
- emit "; call #{func} #{args.join ' '}\n"
106
149
  # First couple of arguments go in registers
107
- register_args = args[0..(number_of_register_arguments - 1)] || []
150
+ register_args = args[0..(@NREG_ARGS - 1)] || []
108
151
  # Rest of arguments go on the stack
109
- stack_args = args[number_of_register_arguments..-1] || []
110
- emit "; register_args: #{register_args.inspect}\n"
111
- emit "; stack_args: #{stack_args.inspect}\n"
152
+ stack_args = args[@NREG_ARGS..-1] || []
112
153
  # Push stack arguments
113
154
  stack_args.reverse.each { |arg| push_qword arg }
114
155
  # Load register arguments
@@ -120,13 +161,15 @@ module Voodoo
120
161
  end
121
162
  end
122
163
  # Call function
123
- value_ref = load_value func, @SCRATCH_REG
124
- emit "xor rax, rax\n"
125
- # If value_ref is a symbol, use PLT-relative addressing
126
- if global?(func)
127
- emit "call #{value_ref} wrt ..plt\n"
128
- else
129
- emit "call #{value_ref}\n"
164
+ with_temporary do |temporary|
165
+ value_ref = load_value func, temporary
166
+ emit "xor rax, rax\n"
167
+ # If value_ref is a symbol, use PLT-relative addressing
168
+ if global?(func)
169
+ emit "call #{value_ref} wrt ..plt\n"
170
+ else
171
+ emit "call #{value_ref}\n"
172
+ end
130
173
  end
131
174
  # Clean up stack
132
175
  unless stack_args.empty?
@@ -134,20 +177,33 @@ module Voodoo
134
177
  end
135
178
  end
136
179
 
137
- # Emit function prologue.
138
- def emit_function_prologue formals = []
139
- emit "push rbp\nmov rbp, rsp\n"
140
- unless formals.empty?
141
- register_args = formals[0...number_of_register_arguments]
142
- register_args.each_with_index do |arg,i|
143
- emit "push #{@ARG_REGS[i]}\n"
144
- end
180
+ # Ends a function body.
181
+ def end_function
182
+ label @function_end_label
183
+ # Restore saved registers
184
+ @saved_registers.each_with_index do |register,i|
185
+ ref = offset_reference saved_local_offset(i)
186
+ emit "mov #{register}, #{ref}\n"
187
+ end
188
+ # Destroy frame.
189
+ emit "leave\n"
190
+ # Return.
191
+ emit "ret\n"
192
+ if @environment == @top_level
193
+ raise "Cannot end function when not in a function"
194
+ else
195
+ @environment = @top_level
145
196
  end
146
197
  end
147
198
 
148
- # Call a function, re-using the current call frame if possible.
199
+ # Returns from a function.
200
+ def ret *words
201
+ eval_expr words, @AX unless words.empty?
202
+ goto @function_end_label
203
+ end
204
+
205
+ # Calls a function, re-using the current call frame if possible.
149
206
  def tail_call func, *args
150
- emit "; tail-call #{func} #{args.join ' '}\n"
151
207
  # Compute required number of stack words
152
208
  nstackargs = number_of_stack_arguments args.length
153
209
  # If we need more stack arguments than we have now,
@@ -155,62 +211,69 @@ module Voodoo
155
211
  if nstackargs > number_of_stack_arguments(@environment.args)
156
212
  emit "; Not enough space for proper tail call; using regular call\n"
157
213
  ret :call, func, *args
158
- end
214
+ else
159
215
 
160
- # If any arguments are going to be overwritten before they are
161
- # used, save them to new local variables and use those instead.
162
- i = args.length - 1
163
- while i >= -1
164
- arg = (i >= 0) ? args[i] : func
216
+ # If any arguments are going to be overwritten before they are
217
+ # used, save them to new local variables and use those instead.
218
+ (args.length - 1).downto(@NREG_ARGS) do |i|
219
+ arg = args[i]
220
+ next unless symbol?(arg)
221
+ old_arg_offset = @environment[arg]
222
+ next if old_arg_offset == nil || old_arg_offset < 0
223
+ # arg is an argument that was passed on the stack.
224
+ new_arg_offset = arg_offset i
225
+ next unless old_arg_offset > new_arg_offset
226
+ # arg will be overwritten before it is used.
227
+ # Save it in a newly created temporary variable,
228
+ # then use that instead.
229
+ newsym = @environment.gensym
230
+ let newsym, arg
231
+ args[i] = newsym
232
+ end
165
233
 
166
- if symbol?(arg)
167
- x = @environment[arg]
168
- if x && x[0] == :arg && x[1] < args.length && x[1] > i &&
169
- (i >= 0 || func != args[x[1]])
170
- # Save value
234
+ # Same for the function we will be calling.
235
+ if symbol?(func)
236
+ offset = @environment[func]
237
+ if offset != nil && offset > 0
171
238
  newsym = @environment.gensym
172
- let newsym, arg
173
- # Change reference
174
- if i >= 0
175
- args[i] = newsym
176
- else
177
- func = newsym
178
- end
239
+ let newsym, func
240
+ func = newsym
179
241
  end
180
242
  end
181
- i = i - 1
182
- end
183
243
 
184
- # Set stack arguments
185
- if args.length > number_of_register_arguments
186
- (args.length - 1).downto(number_of_register_arguments).each do |i|
187
- arg = args[i]
188
-
189
- value_ref = load_value arg, @SCRATCH_REG
190
- newarg_ref = load_arg i
191
- # Elide code if source is same as destination
192
- unless value_ref == newarg_ref
193
- emit "mov #{@SCRATCH_REG}, #{value_ref}\n"
194
- emit "mov #{newarg_ref}, #{@SCRATCH_REG}\n"
244
+ # Set stack arguments
245
+ if args.length > @NREG_ARGS
246
+ (args.length - 1).downto(@NREG_ARGS).each do |i|
247
+ arg = args[i]
248
+
249
+ with_temporary do |temporary|
250
+ value_ref = load_value arg, temporary
251
+ newarg_ref = load_arg i
252
+ # Elide code if source is same as destination
253
+ unless value_ref == newarg_ref
254
+ emit "mov #{temporary}, #{value_ref}\n"
255
+ emit "mov #{newarg_ref}, #{temporary}\n"
256
+ end
257
+ end
195
258
  end
196
259
  end
197
- end
198
260
 
199
- # Set register arguments
200
- number_of_register_arguments(args.length).times do |i|
201
- register = @ARG_REGS[i]
202
- load_value_into_register args[i], register
203
- end
261
+ # Set register arguments
262
+ number_of_register_arguments(args.length).times do |i|
263
+ register = @ARG_REGS[i]
264
+ load_value_into_register args[i], register
265
+ end
204
266
 
205
- # Tail call
206
- func_ref = load_value func, @BX
207
- emit "leave\n"
208
- set_register @AX, 0
209
- # If func_ref is a symbol, use PLT-relative addressing
210
- if global?(func)
211
- emit "jmp #{func_ref} wrt ..plt\n"
212
- else
213
- emit "jmp #{func_ref}\n"
267
+ # Tail call
268
+ func_ref = load_value func, @BX
269
+ emit "leave\n"
270
+ set_register @AX, 0
271
+ # If func_ref is a symbol, use PLT-relative addressing
272
+ if global?(func)
273
+ emit "jmp #{func_ref} wrt ..plt\n"
274
+ else
275
+ emit "jmp #{func_ref}\n"
276
+ end
214
277
  end
215
278
  end
216
279
 
@@ -218,8 +281,8 @@ module Voodoo
218
281
  # == Loading Values
219
282
  #
220
283
 
221
- # Load the value of the nth argument
222
- def load_arg n, reg = @SCRATCH_REG
284
+ # Loads the value of the nth argument.
285
+ def load_arg n
223
286
  if register_argument?(n)
224
287
  # Arguments that were originally passed in a register
225
288
  # are now below rbp
@@ -227,58 +290,68 @@ module Voodoo
227
290
  else
228
291
  # Arguments that were originally passed on the stack
229
292
  # are now above rbp, starting 2 words above it
230
- "[rbp + #{(n + 2 - number_of_register_arguments) * @WORDSIZE}]"
293
+ "[rbp + #{(n + 2 - @NREG_ARGS) * @WORDSIZE}]"
231
294
  end
232
295
  end
233
296
 
234
- # Load the value of the nth local variable
235
- def load_local n, reg = @SCRATCH_REG
236
- # If there current function has any arguments,
237
- # local variables are offset by
238
- # number_of_register_arguments(number_of_arguments)
239
- # words.
240
- offset = number_of_register_arguments(@environment.args) * @WORDSIZE
241
- "[rbp - #{offset + (n + 1) * @WORDSIZE}]"
297
+ # Loads a symbol from the global offset table.
298
+ def load_symbol_from_got symbol, reg
299
+ "[rel #{symbol} wrt ..gotpc]"
242
300
  end
243
301
 
244
302
  #
245
- # == Variables
303
+ # == Miscellaneous
246
304
  #
247
305
 
248
- # Introduce a new local variable
249
- def let symbol, *words
250
- emit "; let #{symbol} #{words.join ' '}\n"
251
- @environment.add_local symbol
252
- eval_expr words, @RETURN_REG
253
- emit "push #{@RETURN_REG}\n"
306
+ # Returns the offset from rbp at which the nth argument is stored.
307
+ def arg_offset n
308
+ if n < @NREG_ARGS
309
+ (n + 1) * -@WORDSIZE
310
+ else
311
+ (n - @NREG_ARGS) * @WORDSIZE + 2 * @WORDSIZE
312
+ end
254
313
  end
255
314
 
256
- #
257
- # == Miscellaneous
258
- #
315
+ # If the nth local is stored in a register, returns that register.
316
+ # Otherwise, returns the offset from the frame pointer.
317
+ def local_offset_or_register n
318
+ if n < @NLOCAL_REGISTERS
319
+ @LOCAL_REGISTERS[n]
320
+ else
321
+ (n + number_of_register_arguments + 1) * -@WORDSIZE
322
+ end
323
+ end
259
324
 
260
- # Load a value and push it on the stack.
325
+ # Loads a value and push it on the stack.
261
326
  def push_qword value
262
- value_ref = load_value value, @SCRATCH_REG
263
- emit "push qword #{value_ref}\n"
327
+ with_temporary do |temporary|
328
+ value_ref = load_value value, temporary
329
+ emit "push qword #{value_ref}\n"
330
+ end
264
331
  end
265
332
 
266
- # Calculate the number of register arguments,
333
+ # Calculates the number of register arguments,
267
334
  # given the total number of arguments.
268
- # If _n_ is +nil+, returns the maximum number of
269
- # register arguments.
270
- def number_of_register_arguments n = nil
271
- if n.nil?
272
- @ARG_REGS.length
273
- else
274
- [@ARG_REGS.length, n].min
275
- end
335
+ def number_of_register_arguments n = @environment.args
336
+ n < @NREG_ARGS ? n : @NREG_ARGS
276
337
  end
277
338
 
278
- # Calculate the number of stack arguments,
339
+ # Calculates the number of locals that are stored in registers.
340
+ def number_of_register_locals n = @environment.locals
341
+ n < @NLOCAL_REGISTERS ? n : @NLOCAL_REGISTERS
342
+ end
343
+
344
+ # Calculates the number of stack arguments,
279
345
  # given the total number of arguments.
280
- def number_of_stack_arguments n
281
- [0, n - number_of_register_arguments].max
346
+ def number_of_stack_arguments n = @environment.args
347
+ x = n - @NREG_ARGS
348
+ x < 0 ? 0 : x
349
+ end
350
+
351
+ # Calculates the number of locals that are stored on the stack.
352
+ def number_of_stack_locals n = @environment.locals
353
+ x = n - @NLOCAL_REGISTERS
354
+ x < 0 ? 0 : x
282
355
  end
283
356
 
284
357
  # Tests if the nth argument is a register argument.
@@ -286,6 +359,11 @@ module Voodoo
286
359
  n < number_of_register_arguments
287
360
  end
288
361
 
362
+ # Returns the offset of the nth saved local.
363
+ def saved_local_offset n
364
+ (number_of_register_arguments + n + 1) * -@WORDSIZE
365
+ end
366
+
289
367
  end
290
368
 
291
369
  # Register class