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