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.
- data/bin/voodooc +85 -49
- data/lib/voodoo.rb +11 -6
- data/lib/voodoo/code_generator.rb +6 -6
- data/lib/voodoo/compiler.rb +2 -2
- data/lib/voodoo/config.rb +3 -3
- data/lib/voodoo/generators/amd64_nasm_generator.rb +196 -118
- data/lib/voodoo/generators/arm_gas_generator.rb +367 -334
- data/lib/voodoo/generators/common_code_generator.rb +467 -69
- data/lib/voodoo/generators/dummy_generator.rb +10 -0
- data/lib/voodoo/generators/i386_nasm_generator.rb +93 -60
- data/lib/voodoo/generators/mips_gas_generator.rb +275 -265
- data/lib/voodoo/generators/nasm_generator.rb +253 -251
- data/lib/voodoo/parser.rb +46 -22
- data/lib/voodoo/validator.rb +118 -60
- metadata +14 -14
- data/lib/voodoo/generators/arm_gas_generator.rb~ +0 -957
data/bin/voodooc
CHANGED
@@ -3,9 +3,10 @@
|
|
3
3
|
require 'getoptlong'
|
4
4
|
|
5
5
|
require 'voodoo'
|
6
|
+
require 'voodoo/generators/dummy_generator'
|
6
7
|
|
7
|
-
|
8
|
-
|
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
|
-
|
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
|
-
['--
|
59
|
-
['--
|
108
|
+
['--arch', '--architecture', '-a', GetoptLong::REQUIRED_ARGUMENT],
|
109
|
+
['--check', '-c', GetoptLong::NO_ARGUMENT],
|
60
110
|
['--features', GetoptLong::NO_ARGUMENT],
|
61
|
-
['--
|
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
|
-
|
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
|
-
|
175
|
-
|
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
|
data/lib/voodoo.rb
CHANGED
@@ -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
|
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
|
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
|
-
#
|
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
|
-
#
|
27
|
-
# example, you could use the code generator to generate native
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
data/lib/voodoo/compiler.rb
CHANGED
@@ -23,7 +23,7 @@ module Voodoo
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
#
|
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
|
-
#
|
39
|
+
# Performs the compilation.
|
40
40
|
def compile
|
41
41
|
section = :code
|
42
42
|
errors = []
|
data/lib/voodoo/config.rb
CHANGED
@@ -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.
|
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 = "
|
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
|
-
#
|
49
|
-
#
|
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
|
-
@
|
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 =
|
67
|
-
#
|
68
|
-
@
|
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 = [
|
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 =
|
90
|
+
@AX = :rax
|
73
91
|
# Base index
|
74
|
-
@BX =
|
92
|
+
@BX = :rbx
|
75
93
|
# Count index
|
76
|
-
@CX =
|
94
|
+
@CX = :rcx
|
77
95
|
# Data index
|
78
|
-
@DX =
|
96
|
+
@DX = :rdx
|
79
97
|
# Base pointer
|
80
|
-
@BP =
|
98
|
+
@BP = :rbp
|
81
99
|
# Stack pointer
|
82
|
-
@SP =
|
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
|
-
#
|
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..(
|
150
|
+
register_args = args[0..(@NREG_ARGS - 1)] || []
|
108
151
|
# Rest of arguments go on the stack
|
109
|
-
stack_args = args[
|
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
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
#
|
138
|
-
def
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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
|
-
#
|
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
|
-
|
214
|
+
else
|
159
215
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
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,
|
173
|
-
|
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
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
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
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
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
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
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
|
-
#
|
222
|
-
def load_arg n
|
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 -
|
293
|
+
"[rbp + #{(n + 2 - @NREG_ARGS) * @WORDSIZE}]"
|
231
294
|
end
|
232
295
|
end
|
233
296
|
|
234
|
-
#
|
235
|
-
def
|
236
|
-
#
|
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
|
-
# ==
|
303
|
+
# == Miscellaneous
|
246
304
|
#
|
247
305
|
|
248
|
-
#
|
249
|
-
def
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
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
|
-
#
|
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
|
-
#
|
325
|
+
# Loads a value and push it on the stack.
|
261
326
|
def push_qword value
|
262
|
-
|
263
|
-
|
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
|
-
#
|
333
|
+
# Calculates the number of register arguments,
|
267
334
|
# given the total number of arguments.
|
268
|
-
|
269
|
-
|
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
|
-
#
|
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
|
-
|
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
|