voodoo 1.0.2 → 1.1.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.
@@ -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