voodoo 0.5.0 → 0.6.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 +2 -2
- data/lib/voodoo/config.rb +43 -3
- data/lib/voodoo/generators/amd64_elf_generator.rb +6 -2
- data/lib/voodoo/generators/amd64_nasm_generator.rb +9 -10
- data/lib/voodoo/generators/common_code_generator.rb +16 -4
- data/lib/voodoo/generators/gas_elf_generator.rb +54 -0
- data/lib/voodoo/generators/i386_elf_generator.rb +7 -37
- data/lib/voodoo/generators/i386_nasm_generator.rb +1 -1
- data/lib/voodoo/generators/mips_elf_generator.rb +46 -0
- data/lib/voodoo/generators/mips_gas_generator.rb +797 -59
- data/lib/voodoo/generators/nasm_elf_generator.rb +6 -1
- data/lib/voodoo/generators/nasm_generator.rb +3 -7
- metadata +4 -3
- data/lib/voodoo/generators/gas_generator.rb +0 -91
data/bin/voodooc
CHANGED
@@ -66,14 +66,14 @@ end
|
|
66
66
|
if architecture == :help
|
67
67
|
# List supported architectures
|
68
68
|
puts "Supported architectures:"
|
69
|
-
puts Voodoo::CodeGenerator.architectures
|
69
|
+
puts Voodoo::CodeGenerator.architectures.map{|arch| arch.to_s}.sort
|
70
70
|
exit
|
71
71
|
end
|
72
72
|
|
73
73
|
if output_format == :help
|
74
74
|
# List supported output formats
|
75
75
|
puts "Supported output formats for architecture #{architecture}:"
|
76
|
-
puts Voodoo::CodeGenerator.formats
|
76
|
+
puts Voodoo::CodeGenerator.formats(architecture).map{|f| f.to_s}.sort
|
77
77
|
exit
|
78
78
|
end
|
79
79
|
|
data/lib/voodoo/config.rb
CHANGED
@@ -4,14 +4,47 @@ module Voodoo
|
|
4
4
|
# Class that holds configuration parameters
|
5
5
|
class Configuration
|
6
6
|
def initialize
|
7
|
-
@default_architecture = :
|
7
|
+
@default_architecture = :auto
|
8
8
|
@default_format = :elf
|
9
|
+
@gas_command = "/usr/bin/as"
|
9
10
|
@nasm_command = "/usr/bin/nasm"
|
10
11
|
end
|
11
12
|
|
12
|
-
attr_accessor :
|
13
|
-
:
|
13
|
+
attr_accessor :default_format,
|
14
|
+
:gas_command,
|
14
15
|
:nasm_command
|
16
|
+
|
17
|
+
attr_writer :default_architecture
|
18
|
+
|
19
|
+
# Returns the default architecture for this compiler
|
20
|
+
def default_architecture
|
21
|
+
if @default_architecture == :auto
|
22
|
+
# If @default_architecture has been set to :auto,
|
23
|
+
# return the host architecture, falling back to :i386
|
24
|
+
# if the host architecture cannot be determined
|
25
|
+
host_architecture || :i386
|
26
|
+
else
|
27
|
+
@default_architecture
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the architecture of the machine running the program,
|
32
|
+
# or nil if that architecture cannot be determined
|
33
|
+
def host_architecture
|
34
|
+
arch, = RUBY_PLATFORM.split '-'
|
35
|
+
case arch
|
36
|
+
when 'x86_64', 'amd64'
|
37
|
+
:amd64
|
38
|
+
when 'i386', 'i686'
|
39
|
+
:i386
|
40
|
+
when 'mips'
|
41
|
+
:mips
|
42
|
+
when 'mipsel'
|
43
|
+
:mipsel
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
15
48
|
end
|
16
49
|
|
17
50
|
DEFAULT_CONFIGURATION = Configuration.new
|
@@ -32,6 +65,13 @@ module Voodoo
|
|
32
65
|
def default_format= value
|
33
66
|
DEFAULT_CONFIGURATION.default_format = value
|
34
67
|
end
|
68
|
+
def gas_command
|
69
|
+
DEFAULT_CONFIGURATION.gas_command
|
70
|
+
end
|
71
|
+
|
72
|
+
def gas_command= value
|
73
|
+
DEFAULT_CONFIGURATION.gas_command = value
|
74
|
+
end
|
35
75
|
def nasm_command
|
36
76
|
DEFAULT_CONFIGURATION.nasm_command
|
37
77
|
end
|
@@ -13,8 +13,12 @@ module Voodoo
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def output_file_name input_name
|
16
|
-
|
17
|
-
end
|
16
|
+
@elfgenerator.output_file_name input_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def output_file_suffix
|
20
|
+
@elfgenerator.output_file_suffix
|
21
|
+
end
|
18
22
|
|
19
23
|
def write io
|
20
24
|
@elfgenerator.write io
|
@@ -96,7 +96,7 @@ module Voodoo
|
|
96
96
|
def call func, *args
|
97
97
|
emit "; call #{func} #{args.join ' '}\n"
|
98
98
|
# First couple of arguments go in registers
|
99
|
-
register_args = args[0..number_of_register_arguments] || []
|
99
|
+
register_args = args[0..(number_of_register_arguments - 1)] || []
|
100
100
|
# Rest of arguments go on the stack
|
101
101
|
stack_args = args[number_of_register_arguments..-1] || []
|
102
102
|
emit "; register_args: #{register_args.inspect}\n"
|
@@ -175,24 +175,23 @@ module Voodoo
|
|
175
175
|
|
176
176
|
# Set stack arguments
|
177
177
|
if args.length > number_of_register_arguments
|
178
|
-
(args.length - 1
|
178
|
+
(args.length - 1).downto(number_of_register_arguments).each do |i|
|
179
179
|
arg = args[i]
|
180
180
|
|
181
181
|
value_ref = load_value arg, @SCRATCH_REG
|
182
182
|
newarg_ref = load_arg i
|
183
183
|
# Elide code if source is same as destination
|
184
184
|
unless value_ref == newarg_ref
|
185
|
-
emit "mov #{@
|
185
|
+
emit "mov #{@SCRATCH_REG}, #{value_ref}\n"
|
186
|
+
emit "mov #{newarg_ref}, #{@SCRATCH_REG}\n"
|
186
187
|
end
|
187
188
|
end
|
188
189
|
end
|
189
190
|
|
190
191
|
# Set register arguments
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
load_value_into_register args[i], register
|
195
|
-
end
|
192
|
+
number_of_register_arguments(args.length).times do |i|
|
193
|
+
register = @ARG_REGS[i]
|
194
|
+
load_value_into_register args[i], register
|
196
195
|
end
|
197
196
|
|
198
197
|
# Tail call
|
@@ -219,8 +218,8 @@ module Voodoo
|
|
219
218
|
"[rbp - #{(n + 1) * @WORDSIZE}]"
|
220
219
|
else
|
221
220
|
# Arguments that were originally passed on the stack
|
222
|
-
# are now above rbp
|
223
|
-
"[rbp + #{(n +
|
221
|
+
# are now above rbp, starting 2 words above it
|
222
|
+
"[rbp + #{(n + 2 - number_of_register_arguments) * @WORDSIZE}]"
|
224
223
|
end
|
225
224
|
end
|
226
225
|
|
@@ -10,6 +10,7 @@ module Voodoo
|
|
10
10
|
# - #add_function
|
11
11
|
# - #gensym
|
12
12
|
# - #output_file_name
|
13
|
+
# - #output_file_suffix
|
13
14
|
# - #wordsize
|
14
15
|
# - #write
|
15
16
|
#
|
@@ -41,6 +42,7 @@ module Voodoo
|
|
41
42
|
self.section = :code
|
42
43
|
@top_level = Environment.initial_environment
|
43
44
|
@environment = @top_level
|
45
|
+
@output_file_suffix = '.o'
|
44
46
|
end
|
45
47
|
|
46
48
|
# Adds code to the given section.
|
@@ -81,9 +83,7 @@ module Voodoo
|
|
81
83
|
keyword, args = action[0], action[1..-1]
|
82
84
|
case keyword
|
83
85
|
when :function
|
84
|
-
|
85
|
-
args[1..-1].each { |action| add section, action }
|
86
|
-
end_function
|
86
|
+
function args[0], *args[1..-1]
|
87
87
|
when :ifeq, :ifge, :ifgt, :ifle, :iflt, :ifne
|
88
88
|
truebody = action[2]
|
89
89
|
falsebody = action[3]
|
@@ -121,6 +121,13 @@ module Voodoo
|
|
121
121
|
add :functions, [:function, formals] + code
|
122
122
|
end
|
123
123
|
|
124
|
+
# Add a function to the current section
|
125
|
+
def function formals, *code
|
126
|
+
begin_function *formals
|
127
|
+
code.each { |action| add section, action }
|
128
|
+
end_function
|
129
|
+
end
|
130
|
+
|
124
131
|
# Generate a new, unused symbol
|
125
132
|
def gensym
|
126
133
|
Environment.gensym
|
@@ -179,7 +186,12 @@ module Voodoo
|
|
179
186
|
# Given an input file name, returns the canonical output file name
|
180
187
|
# for this code generator.
|
181
188
|
def output_file_name input_name
|
182
|
-
input_name.sub(/\.voo$/, '') +
|
189
|
+
input_name.sub(/\.voo$/, '') + @output_file_suffix
|
190
|
+
end
|
191
|
+
|
192
|
+
# Returns the canonical output file suffix for this code generator
|
193
|
+
def output_file_suffix
|
194
|
+
@output_file_suffix
|
183
195
|
end
|
184
196
|
|
185
197
|
class Environment
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'voodoo/config'
|
2
|
+
require 'voodoo/generators/command_postprocessor'
|
3
|
+
|
4
|
+
module Voodoo
|
5
|
+
# Class that generates ELF object files by invoking the GNU assembler on
|
6
|
+
# the output of a code generator that generates GNU assembly.
|
7
|
+
class GasELFGenerator
|
8
|
+
def initialize asmgenerator, extra_args
|
9
|
+
@asmgenerator = asmgenerator
|
10
|
+
@extra_args = extra_args
|
11
|
+
@output_file_suffix = '.o'
|
12
|
+
end
|
13
|
+
|
14
|
+
include CommandPostProcessor
|
15
|
+
|
16
|
+
def output_file_name input_name
|
17
|
+
input_name.sub(/\.voo$/, '') + @output_file_suffix
|
18
|
+
end
|
19
|
+
|
20
|
+
def output_file_suffix
|
21
|
+
@output_file_suffix
|
22
|
+
end
|
23
|
+
|
24
|
+
# Writes the generated code to the given IO handle
|
25
|
+
def write io
|
26
|
+
# Create temporary file to write assembly code to
|
27
|
+
if io.respond_to? :path
|
28
|
+
base = File.basename(io.path).sub(/([^.]*).*/, "\\1")
|
29
|
+
else
|
30
|
+
base = self.class.name
|
31
|
+
end
|
32
|
+
|
33
|
+
Tempfile.open(base + '.s') do |asmfile|
|
34
|
+
Tempfile.open(base + '.o') do |elffile|
|
35
|
+
elffile.close
|
36
|
+
# Write assembly code to nsamfile
|
37
|
+
@asmgenerator.write asmfile
|
38
|
+
asmfile.close
|
39
|
+
# Find out the name of the GNU assembler
|
40
|
+
gas = Voodoo::Config.gas_command
|
41
|
+
# Invoke gas on asmfile
|
42
|
+
command = "#{gas} #{@extra_args}" +
|
43
|
+
" -o #{shell_encode elffile.path}" +
|
44
|
+
" #{shell_encode asmfile.path}"
|
45
|
+
if system(command)
|
46
|
+
write_file_to_io elffile.path, io
|
47
|
+
end
|
48
|
+
elffile.unlink
|
49
|
+
end
|
50
|
+
asmfile.unlink
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,57 +1,27 @@
|
|
1
1
|
require 'delegate'
|
2
2
|
require 'tempfile'
|
3
|
-
require 'voodoo/config'
|
4
3
|
require 'voodoo/generators/i386_nasm_generator'
|
4
|
+
require 'voodoo/generators/nasm_elf_generator'
|
5
5
|
|
6
6
|
module Voodoo
|
7
|
-
# Generator that produces ELF objects
|
7
|
+
# Generator that produces ELF objects for i386
|
8
8
|
class I386ELFGenerator < DelegateClass(I386NasmGenerator)
|
9
9
|
def initialize params = {}
|
10
10
|
@nasmgenerator = I386NasmGenerator.new params
|
11
11
|
super(@nasmgenerator)
|
12
|
+
@elfgenerator = NasmELFGenerator.new @nasmgenerator, '-f elf32'
|
12
13
|
end
|
13
14
|
|
14
15
|
def output_file_name input_name
|
15
|
-
|
16
|
+
@elfgenerator.output_file_name input_name
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
'"' + string.gsub(/([\\`"$\n])/, "\\\\\\1") + '"'
|
19
|
+
def output_file_suffix
|
20
|
+
@elfgenerator.output_file_suffix
|
21
21
|
end
|
22
22
|
|
23
|
-
# Writes the generated code to the given IO handle
|
24
23
|
def write io
|
25
|
-
|
26
|
-
if io.respond_to? :path
|
27
|
-
basename = File.basename(io.path).sub(/([^.]*).*/, "\\1")
|
28
|
-
else
|
29
|
-
basename = self.class.name
|
30
|
-
end
|
31
|
-
Tempfile.open(basename + '.asm') do |nasmfile|
|
32
|
-
Tempfile.open(basename + '.o') do |elffile|
|
33
|
-
elffile.close
|
34
|
-
@nasmgenerator.write nasmfile
|
35
|
-
nasmfile.close
|
36
|
-
# Invoke nasm on nasmfile
|
37
|
-
if ENV.has_key? 'NASM'
|
38
|
-
nasm = ENV['NASM']
|
39
|
-
elsif ENV.has_key? 'YASM'
|
40
|
-
nasm = ENV['YASM']
|
41
|
-
else
|
42
|
-
nasm = Config.nasm_command
|
43
|
-
end
|
44
|
-
command = "#{nasm} -f elf -o #{shell_encode elffile.path}" +
|
45
|
-
" #{shell_encode nasmfile.path}"
|
46
|
-
if system(command)
|
47
|
-
File.open(elffile.path) do |file|
|
48
|
-
io.write file.read
|
49
|
-
end
|
50
|
-
end
|
51
|
-
elffile.unlink
|
52
|
-
end
|
53
|
-
nasmfile.unlink
|
54
|
-
end
|
24
|
+
@elfgenerator.write io
|
55
25
|
end
|
56
26
|
end
|
57
27
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'voodoo/generators/mips_gas_generator'
|
4
|
+
require 'voodoo/generators/gas_elf_generator'
|
5
|
+
|
6
|
+
module Voodoo
|
7
|
+
# Generator that produces ELF objects for mips and mipsel
|
8
|
+
class MIPSELFGenerator < DelegateClass(MIPSGasGenerator)
|
9
|
+
def initialize params = {}
|
10
|
+
@asmgenerator = MIPSGasGenerator.new params
|
11
|
+
super(@asmgenerator)
|
12
|
+
case params[:architecture]
|
13
|
+
when :mips
|
14
|
+
byte_order = ' -EB'
|
15
|
+
when :mipsel
|
16
|
+
byte_order = ' -EL'
|
17
|
+
else
|
18
|
+
byte_order = ''
|
19
|
+
end
|
20
|
+
opts = '-KPIC' + byte_order
|
21
|
+
@elfgenerator = GasELFGenerator.new @asmgenerator, opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def output_file_name input_name
|
25
|
+
@elfgenerator.output_file_name input_name
|
26
|
+
end
|
27
|
+
|
28
|
+
def output_file_suffix
|
29
|
+
@elfgenerator.output_file_suffix
|
30
|
+
end
|
31
|
+
|
32
|
+
def write io
|
33
|
+
@elfgenerator.write io
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Register class for mips
|
38
|
+
Voodoo::CodeGenerator.register_generator MIPSELFGenerator,
|
39
|
+
:architecture => :mips,
|
40
|
+
:format => :elf
|
41
|
+
|
42
|
+
# Register class for mipsel
|
43
|
+
Voodoo::CodeGenerator.register_generator MIPSELFGenerator,
|
44
|
+
:architecture => :mipsel,
|
45
|
+
:format => :elf
|
46
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'voodoo/generators/
|
1
|
+
require 'voodoo/generators/common_code_generator'
|
2
2
|
|
3
3
|
module Voodoo
|
4
4
|
# = MIPS GNU Assembler Code Generator
|
@@ -9,9 +9,19 @@ module Voodoo
|
|
9
9
|
# == Calling Convention
|
10
10
|
#
|
11
11
|
# The first four arguments are passed in the registers $4 through $7.
|
12
|
-
# Any additional arguments are passed
|
12
|
+
# Any additional arguments are passed on the stack, starting at
|
13
|
+
# $sp + 16. Words $sp through $sp + 12 will be available for the called
|
14
|
+
# function to use. $sp will always be a multiple of 8.
|
13
15
|
#
|
14
|
-
# The return
|
16
|
+
# The return address for the called function is passed in $31.
|
17
|
+
#
|
18
|
+
# When performing a position-independent call, the address of the called
|
19
|
+
# function is passed in $25.
|
20
|
+
#
|
21
|
+
# The called function will store its return value in $2.
|
22
|
+
#
|
23
|
+
# The called function is required to preserve the values of registers
|
24
|
+
# $16 through $23 and register $30.
|
15
25
|
#
|
16
26
|
# This calling convention is compatible with the System V ELF ABI.
|
17
27
|
#
|
@@ -32,104 +42,592 @@ module Voodoo
|
|
32
42
|
# empty1
|
33
43
|
# empty0 <-- $sp points here
|
34
44
|
#
|
35
|
-
#
|
45
|
+
# The function prologue of functions generated by this code generator
|
46
|
+
# creates activiation frames that look as follows:
|
36
47
|
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
48
|
+
# :
|
49
|
+
# old frame
|
50
|
+
# argn
|
51
|
+
# :
|
52
|
+
# arg4
|
53
|
+
# arg3
|
54
|
+
# arg2
|
55
|
+
# arg1
|
56
|
+
# arg0
|
57
|
+
# return address
|
58
|
+
# pointer to Global Offset Table
|
59
|
+
# local0
|
60
|
+
# :
|
61
|
+
# local7
|
62
|
+
# local8
|
63
|
+
# :
|
64
|
+
# localn
|
65
|
+
# padding <-- $sp points here
|
41
66
|
#
|
42
|
-
#
|
43
|
-
# - Need space to save $sp, $r, and however many local variables
|
44
|
-
# the function uses
|
45
|
-
# - $sp must be a multiple of 8
|
46
|
-
# 2. Calculate $gp (TODO: how?) and save it somewhere (either on the stack
|
47
|
-
# or in a previously-saved callee-save register)
|
48
|
-
# 3. Save $sp and $r
|
67
|
+
# In words:
|
49
68
|
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
69
|
+
# The four empty slots provided by the caller are used to store
|
70
|
+
# arguments 0 through 3 (originally passed in $4 through $7),
|
71
|
+
# if necessary.
|
53
72
|
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# :
|
59
|
-
# localn <-- $sp points here
|
73
|
+
# The stack frame created below the four slots provided by the caller
|
74
|
+
# contains the following data, in order:
|
75
|
+
#
|
76
|
+
# - Saved return address (originally passed in $31), if necessary.
|
60
77
|
#
|
61
|
-
#
|
78
|
+
# - Saved pointer to global offset table (computed from $25), if necessary.
|
62
79
|
#
|
63
|
-
#
|
80
|
+
# - Saved values of caller's locals 0 through 7
|
81
|
+
# (originally in $16 through $23), if necessary.
|
64
82
|
#
|
65
|
-
#
|
66
|
-
# variables. They are callee-save, which means the value they had upon
|
67
|
-
# function entry must be restored before returning.
|
83
|
+
# - Values of our locals > 8, if necessary.
|
68
84
|
#
|
69
|
-
#
|
70
|
-
# position-independent code is called. Otherwise, it may be used as
|
71
|
-
# a temporary.
|
85
|
+
# - Padding to align $sp on a multiple of 8, if necessary.
|
72
86
|
#
|
73
|
-
# Registers $26 and $27 are reserved for operating system use.
|
74
87
|
#
|
75
|
-
#
|
88
|
+
# In accordance with the System V ELF ABI for MIPS, the pointer to
|
89
|
+
# the global offset table is calculated as follows:
|
76
90
|
#
|
77
|
-
#
|
78
|
-
# value it has upon function entry must be restored before returning.
|
79
|
-
# The stack pointer must always be a multiple of 8.
|
91
|
+
# $gp = _gp_disp + $25
|
80
92
|
#
|
81
|
-
#
|
93
|
+
# where $gp is the register to store the pointer, $25 is the register
|
94
|
+
# holding the address of the called function, and _gp_disp is the
|
95
|
+
# offset between the beginning of the function and the global offset table.
|
82
96
|
#
|
83
|
-
|
97
|
+
class MIPSGasGenerator < CommonCodeGenerator
|
84
98
|
def initialize params
|
85
99
|
@WORDSIZE = 4
|
86
100
|
@CODE_ALIGNMENT = 0
|
87
101
|
@DATA_ALIGNMENT = @WORDSIZE
|
88
102
|
@FUNCTION_ALIGNMENT = @WORDSIZE
|
103
|
+
|
104
|
+
@INITIAL_FRAME_SIZE = 2 * @WORDSIZE
|
105
|
+
@REGISTER_ARG_BASE = 4
|
106
|
+
@NREGISTER_ARGS = 4
|
107
|
+
@REGISTER_LOCAL_BASE = 16
|
108
|
+
@NREGISTER_LOCALS = 8
|
109
|
+
@RETURN = :'$2'
|
110
|
+
@TEMPORARY = :'$1'
|
111
|
+
@FUNCTION = :'$25'
|
112
|
+
@GOT = :'$28'
|
113
|
+
@TEMPORARIES = [:'$8', :'$9', :'$10', :'$11',
|
114
|
+
:'$12', :'$13', :'$14', :'$15']
|
115
|
+
@function_end_label = nil
|
116
|
+
@imports = {}
|
117
|
+
@if_labels = []
|
89
118
|
super params
|
119
|
+
@output_file_suffix = '.s'
|
120
|
+
end
|
121
|
+
|
122
|
+
def align alignment = nil
|
123
|
+
unless alignment
|
124
|
+
# Get default alignment
|
125
|
+
case @section
|
126
|
+
when :code
|
127
|
+
alignment = @CODE_ALIGNMENT
|
128
|
+
when :data
|
129
|
+
alignment = @DATA_ALIGNMENT
|
130
|
+
when :function
|
131
|
+
alignment = @FUNCTION_ALIGNMENT
|
132
|
+
else
|
133
|
+
# Use data alignment as default
|
134
|
+
alignment = @DATA_ALIGNMENT
|
135
|
+
end
|
136
|
+
end
|
137
|
+
emit ".align #{alignment}\n" unless alignment == 0
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return the offset from $sp at which the nth (0-based) argument is
|
141
|
+
# stored.
|
142
|
+
def arg_offset n
|
143
|
+
@frame_size + n * @WORDSIZE
|
90
144
|
end
|
91
145
|
|
92
|
-
#
|
93
|
-
|
94
|
-
|
146
|
+
# Return an $sp-relative reference for the nth (0-based) argument
|
147
|
+
def arg_reference n
|
148
|
+
"#{arg_offset n}($sp)"
|
149
|
+
end
|
150
|
+
|
151
|
+
# Return the register in which the nth (0-based) argument is stored, or
|
152
|
+
# nil if not stored in a register
|
153
|
+
def arg_register n
|
154
|
+
if register_arg? n
|
155
|
+
"$#{@REGISTER_ARG_BASE + n}"
|
156
|
+
else
|
157
|
+
nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Test if op is a binary operation
|
162
|
+
def assymetric_binop? op
|
163
|
+
[:div, :mod, :sub].member?(op)
|
164
|
+
end
|
95
165
|
|
96
166
|
# Emit function prologue and declare _formals_ as function arguments
|
97
|
-
def begin_function
|
167
|
+
def begin_function formals, nlocals
|
168
|
+
if @environment != @top_level
|
169
|
+
raise "Cannot begin function when already in a function"
|
170
|
+
end
|
171
|
+
|
98
172
|
emit "# function #{formals.join ' '}\n"
|
99
173
|
environment = Environment.new @environment
|
100
174
|
environment.add_args formals
|
101
175
|
@environment = environment
|
102
|
-
|
176
|
+
@function_end_label = gensym
|
177
|
+
emit_function_prologue formals, nlocals
|
178
|
+
end
|
179
|
+
|
180
|
+
# Test if op is a binary operation
|
181
|
+
def binop? op
|
182
|
+
assymetric_binop?(op) || symmetric_binop?(op)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Define a byte with the given value
|
186
|
+
def byte value
|
187
|
+
emit ".byte #{value}\n"
|
103
188
|
end
|
104
189
|
|
105
190
|
# Call a function.
|
106
191
|
def call func, *args
|
107
|
-
|
192
|
+
emit "# call #{func} #{args.join ' '}\n"
|
193
|
+
# Grow the stack frame, subject to 3 conditions:
|
194
|
+
# 1. There must be enough space to hold all arguments
|
195
|
+
# 2. $sp must remain a multiple of 8
|
196
|
+
# 3. We must provide space for at least 4 words
|
197
|
+
increment = ([args.length, 4].max * @WORDSIZE + 7) / 8 * 8
|
198
|
+
emit "addiu $sp, $sp, -#{increment}\n"
|
199
|
+
@frame_size = @frame_size + increment
|
200
|
+
|
201
|
+
# Put arguments in the right places
|
202
|
+
n = 0
|
203
|
+
while n < args.length
|
204
|
+
if n < @NREGISTER_ARGS
|
205
|
+
# Put arguments up to @NREGISTER_ARGS in registers
|
206
|
+
load_value_into_register args[n], arg_register(n)
|
207
|
+
else
|
208
|
+
# Put other arguments on the stack
|
209
|
+
load_value_into_register args[n], @TEMPORARY
|
210
|
+
emit "sw #{@TEMPORARY}, #{n * @WORDSIZE}($sp)\n"
|
211
|
+
end
|
212
|
+
n = n + 1
|
213
|
+
end
|
214
|
+
|
215
|
+
# Load function address
|
216
|
+
if global? func
|
217
|
+
if @imports.has_key? func
|
218
|
+
# Load address from global offset table
|
219
|
+
emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
|
220
|
+
else
|
221
|
+
# Assume label defined in this module
|
222
|
+
emit "lui #{@FUNCTION}, %hi(#{func})\n"
|
223
|
+
emit "addiu #{@FUNCTION}, %lo(#{func})\n"
|
224
|
+
end
|
225
|
+
else
|
226
|
+
# Load address
|
227
|
+
load_value_into_register func, "#{@FUNCTION}"
|
228
|
+
end
|
229
|
+
|
230
|
+
# Perform call
|
231
|
+
emit "jalr #{@FUNCTION}\n"
|
232
|
+
# brach delay slot
|
233
|
+
emit "nop\n"
|
234
|
+
|
235
|
+
# Restore original stack frame
|
236
|
+
emit "addiu $sp, $sp, #{increment}\n"
|
237
|
+
@frame_size = @frame_size - increment
|
238
|
+
|
239
|
+
# restore gp
|
240
|
+
emit "lw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
|
108
241
|
end
|
109
242
|
|
110
|
-
#
|
111
|
-
|
112
|
-
|
243
|
+
# Start a conditional using the specified branch instruction
|
244
|
+
# after the comparison.
|
245
|
+
def common_if comp, x, y = nil
|
246
|
+
emit "# #{comp} #{x} #{y}\n"
|
247
|
+
|
248
|
+
temporaries = @TEMPORARIES.dup
|
249
|
+
xreg = load_value x, temporaries[0]
|
250
|
+
temporaries.delete xreg
|
251
|
+
yreg = load_value y, temporaries[0]
|
252
|
+
temporaries.delete yreg
|
253
|
+
|
254
|
+
falselabel = @environment.gensym
|
255
|
+
@if_labels.push falselabel
|
256
|
+
|
257
|
+
case comp
|
258
|
+
when :ifeq
|
259
|
+
emit "bne #{xreg}, #{yreg}, #{falselabel}\n"
|
260
|
+
when :ifge
|
261
|
+
emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
|
262
|
+
emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
|
263
|
+
when :ifgt
|
264
|
+
emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
|
265
|
+
emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
|
266
|
+
when :ifle
|
267
|
+
emit "slt #{@TEMPORARY}, #{yreg}, #{xreg}\n"
|
268
|
+
emit "bne #{@TEMPORARY}, $0, #{falselabel}\n"
|
269
|
+
when :iflt
|
270
|
+
emit "slt #{@TEMPORARY}, #{xreg}, #{yreg}\n"
|
271
|
+
emit "beq #{@TEMPORARY}, $0, #{falselabel}\n"
|
272
|
+
when :ifne
|
273
|
+
emit "beq #{xreg}, #{yreg}, #{falselabel}\n"
|
274
|
+
else
|
275
|
+
raise "Unknown conditional: #{comp}"
|
276
|
+
end
|
277
|
+
emit "nop\n"
|
113
278
|
end
|
114
279
|
|
115
280
|
# Emit function prologue.
|
116
|
-
def emit_function_prologue formals = []
|
117
|
-
|
281
|
+
def emit_function_prologue formals = [], nlocals = 0
|
282
|
+
# Calculate new value for $gp
|
283
|
+
emit "lui #{@GOT}, %hi(_gp_disp)\n"
|
284
|
+
emit "addiu #{@GOT}, %lo(_gp_disp)\n"
|
285
|
+
emit "addu #{@GOT}, #{@GOT}, #{@FUNCTION}\n"
|
286
|
+
|
287
|
+
# Save parameters 0 .. 3 in the stack space that the caller
|
288
|
+
# should have allocated for them.
|
289
|
+
[@NREGISTER_ARGS, formals.length].min.times do |n|
|
290
|
+
emit "sw $#{n + @REGISTER_ARG_BASE}, #{n * @WORDSIZE}($29)\n"
|
291
|
+
end
|
292
|
+
|
293
|
+
# Create initial frame
|
294
|
+
@frame_size = (@INITIAL_FRAME_SIZE + nlocals * @WORDSIZE + 7) / 8 * 8
|
295
|
+
emit "addiu $sp, $sp, -#{@frame_size}\n"
|
296
|
+
|
297
|
+
# Save return address
|
298
|
+
emit "sw $31, #{@frame_size - @WORDSIZE}($sp)\n"
|
299
|
+
|
300
|
+
# Save gp
|
301
|
+
emit "sw #{@GOT}, #{@frame_size - 2 * @WORDSIZE}($sp)\n"
|
118
302
|
end
|
119
303
|
|
120
304
|
# End a function body
|
121
305
|
def end_function
|
122
|
-
emit "# end function\n\n"
|
123
306
|
if @environment == @top_level
|
124
307
|
raise "Cannot end function when not in a function"
|
308
|
+
end
|
309
|
+
|
310
|
+
emit "# function epilogue\n"
|
311
|
+
|
312
|
+
label @function_end_label
|
313
|
+
# Restore saved locals
|
314
|
+
[@NREGISTER_LOCALS, @environment.locals].min.times do |n|
|
315
|
+
emit "lw #{local_register n}, #{local_reference n}\n"
|
316
|
+
end
|
317
|
+
# Load return address
|
318
|
+
emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
|
319
|
+
# Restore stack pointer
|
320
|
+
emit "addiu $sp, $sp, #{@frame_size}\n"
|
321
|
+
# Return
|
322
|
+
emit "jr $31\n"
|
323
|
+
emit "nop\n"
|
324
|
+
|
325
|
+
emit "# end function\n\n"
|
326
|
+
|
327
|
+
@function_end_label = nil
|
328
|
+
@environment = @top_level
|
329
|
+
end
|
330
|
+
|
331
|
+
# End a conditional.
|
332
|
+
def end_if
|
333
|
+
label = @if_labels.pop
|
334
|
+
emit "#{label}:\n"
|
335
|
+
end
|
336
|
+
|
337
|
+
# Evaluate the binary operation expr and store the result in register
|
338
|
+
def eval_binop expr, register
|
339
|
+
temporaries = @TEMPORARIES.dup
|
340
|
+
x = load_value expr[1], temporaries[0]
|
341
|
+
temporaries.delete x
|
342
|
+
y = load_value expr[2], temporaries[0]
|
343
|
+
temporaries.delete y
|
344
|
+
|
345
|
+
case expr[0]
|
346
|
+
when :div
|
347
|
+
emit "div $0, #{x}, #{y}\n"
|
348
|
+
emit "mflo #{register}\n"
|
349
|
+
when :mod
|
350
|
+
emit "div $0, #{x}, #{y}\n"
|
351
|
+
emit "mfhi #{register}\n"
|
352
|
+
when :mul
|
353
|
+
emit "mult #{x}, #{y}\n"
|
354
|
+
emit "mflo #{register}\n"
|
125
355
|
else
|
126
|
-
|
356
|
+
emit "#{expr[0]} #{register}, #{x}, #{y}\n"
|
127
357
|
end
|
128
358
|
end
|
129
359
|
|
130
|
-
#
|
131
|
-
def
|
132
|
-
|
360
|
+
# Evaluate the expression expr and store the result in register
|
361
|
+
def eval_expr expr, register
|
362
|
+
if expr.length == 1
|
363
|
+
# Load value
|
364
|
+
load_value_into_register expr[0], register
|
365
|
+
else
|
366
|
+
# Evaluate expression
|
367
|
+
op = expr[0]
|
368
|
+
case op
|
369
|
+
when :call
|
370
|
+
call *expr[1..-1]
|
371
|
+
emit "move #{register}, #{@RETURN}\n" if register != @RETURN
|
372
|
+
when :'get-byte'
|
373
|
+
get_byte expr[1], expr[2], register
|
374
|
+
when :'get-word'
|
375
|
+
get_word expr[1], expr[2], register
|
376
|
+
when :not
|
377
|
+
eval_binop [:nor, 0, expr[1]], register
|
378
|
+
else
|
379
|
+
if binop? op
|
380
|
+
eval_binop expr, register
|
381
|
+
else
|
382
|
+
raise "Not a magic word: #{op}"
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
# Export symbols from the current section
|
389
|
+
def export *symbols
|
390
|
+
symbols.each { |sym| emit ".globl #{sym}\n" }
|
391
|
+
end
|
392
|
+
|
393
|
+
# Add a function to the current section
|
394
|
+
def function formals, *code
|
395
|
+
# Set nlocals to the number of local variables in the function
|
396
|
+
nlocals = 0
|
397
|
+
code.each { |action| nlocals = nlocals + 1 if action[0] == :let }
|
398
|
+
begin_function formals, nlocals
|
399
|
+
code.each { |action| add section, action }
|
400
|
+
end_function
|
401
|
+
end
|
402
|
+
|
403
|
+
# Load byte from _base_ + _offset_ into _register_
|
404
|
+
def get_byte base, offset, register
|
405
|
+
# If base is an integer, but offset isn't, swap them
|
406
|
+
if !integer?(offset) && integer?(base)
|
407
|
+
base, offset = [offset, base]
|
408
|
+
end
|
409
|
+
|
410
|
+
if integer? offset
|
411
|
+
base_reg = load_value base
|
412
|
+
emit "lb #{register}, #{offset}(#{base_reg})\n"
|
413
|
+
else
|
414
|
+
eval_binop [:add, base, offset], @TEMPORARY
|
415
|
+
emit "lb #{register}, 0(#{@TEMPORARY})\n"
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
# Load word from _base_ + _offset_ * _@WORDSIZE_ into _register_
|
420
|
+
def get_word base, offset, register
|
421
|
+
if integer? offset
|
422
|
+
base_reg = load_value base
|
423
|
+
emit "lw #{register}, #{offset * @WORDSIZE}(#{base_reg})\n"
|
424
|
+
else
|
425
|
+
offset_reg = @TEMPORARIES.pop
|
426
|
+
begin
|
427
|
+
load_value_into_register offset, offset_reg
|
428
|
+
base_reg = load_value base
|
429
|
+
emit "sll #{offset_reg}, #{offset_reg}, 2\n" # multiply by @WORDSIZE
|
430
|
+
emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
|
431
|
+
emit "lw #{register}, 0(#{@TEMPORARY})\n"
|
432
|
+
ensure
|
433
|
+
@TEMPORARIES.push offset_reg
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
# Test if a symbol refers to a global
|
439
|
+
def global? symbol
|
440
|
+
symbol?(symbol) && @environment[symbol] == nil
|
441
|
+
end
|
442
|
+
|
443
|
+
# Jump to a label.
|
444
|
+
def goto label
|
445
|
+
emit "j #{label}\n"
|
446
|
+
emit "nop\n"
|
447
|
+
end
|
448
|
+
|
449
|
+
# Start the false path of a conditional.
|
450
|
+
def ifelse
|
451
|
+
emit "# else\n"
|
452
|
+
newlabel = @environment.gensym
|
453
|
+
emit "j #{newlabel}\n"
|
454
|
+
emit "nop\n"
|
455
|
+
label = @if_labels.pop
|
456
|
+
emit "#{label}:\n"
|
457
|
+
@if_labels.push newlabel
|
458
|
+
end
|
459
|
+
|
460
|
+
# Test if x is equal to y
|
461
|
+
def ifeq x, y
|
462
|
+
common_if :ifeq, x, y
|
463
|
+
end
|
464
|
+
|
465
|
+
# Test if x is greater than or equal to y
|
466
|
+
def ifge x, y
|
467
|
+
common_if :ifge, x, y
|
468
|
+
end
|
469
|
+
|
470
|
+
# Test if x is strictly greater than y
|
471
|
+
def ifgt x, y
|
472
|
+
common_if :ifgt, x, y
|
473
|
+
end
|
474
|
+
|
475
|
+
# Test if x is less than or equal to y
|
476
|
+
def ifle x, y
|
477
|
+
common_if :ifle, x, y
|
478
|
+
end
|
479
|
+
|
480
|
+
# Test if x is strictly less than y
|
481
|
+
def iflt x, y
|
482
|
+
common_if :iflt, x, y
|
483
|
+
end
|
484
|
+
|
485
|
+
# Test if x different from y
|
486
|
+
def ifne x, y
|
487
|
+
common_if :ifne, x, y
|
488
|
+
end
|
489
|
+
|
490
|
+
# Import labels into the current section
|
491
|
+
def import *symbols
|
492
|
+
# Record imported labels in @imports
|
493
|
+
symbols.each { |sym| @imports[sym] = sym }
|
494
|
+
end
|
495
|
+
|
496
|
+
# Test if a value is an integer
|
497
|
+
def integer? value
|
498
|
+
value.kind_of? Integer
|
499
|
+
end
|
500
|
+
|
501
|
+
# Emit a label
|
502
|
+
def label name
|
503
|
+
emit "#{name}:\n"
|
504
|
+
end
|
505
|
+
|
506
|
+
# Introduce a new local variable
|
507
|
+
def let symbol, *expr
|
508
|
+
emit "# let #{symbol} #{expr.join ' '}\n"
|
509
|
+
n = @environment.locals
|
510
|
+
@environment.add_local symbol
|
511
|
+
|
512
|
+
# Extend stack frame, if necessary
|
513
|
+
if @environment.locals > max_locals
|
514
|
+
$stderr.puts "WARNING: Stack frame too small. Possible BUG"
|
515
|
+
# Extend frame by 8 bytes, to keep $sp a multiple of 8
|
516
|
+
emit "addiu $sp, $sp, -8\n"
|
517
|
+
@frame_size = @frame_size + 8
|
518
|
+
end
|
519
|
+
|
520
|
+
register = local_register n
|
521
|
+
ref = local_reference n
|
522
|
+
if register
|
523
|
+
# Save current value of register
|
524
|
+
emit "sw #{register}, #{ref}\n"
|
525
|
+
# Set new value
|
526
|
+
eval_expr expr, register
|
527
|
+
else
|
528
|
+
eval_expr expr, @TEMPORARY
|
529
|
+
emit "sw #{@TEMPORARY}, #{ref}\n"
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
# Load a value into a register.
|
534
|
+
# Returns the name of the register.
|
535
|
+
# If the value was already in a register, the name of that
|
536
|
+
# register is returned.
|
537
|
+
# Else, the value is loaded into a register and the name of
|
538
|
+
# that register is returned. The register to use in that case
|
539
|
+
# may be specified using the optional second argument.
|
540
|
+
def load_value x, register = @TEMPORARY
|
541
|
+
if x == 0
|
542
|
+
return "$0"
|
543
|
+
elsif integer? x
|
544
|
+
if x >= -32768 && x <= 32767
|
545
|
+
emit "addiu #{register}, $0, #{x}\n"
|
546
|
+
return register
|
547
|
+
else
|
548
|
+
emit "lui #{register}, #{x >> 16}\n"
|
549
|
+
emit "ori #{register}, #{register}, #{x & 0xffff}\n"
|
550
|
+
return register
|
551
|
+
end
|
552
|
+
elsif symbol? x
|
553
|
+
binding = @environment[x]
|
554
|
+
if binding
|
555
|
+
case binding[0]
|
556
|
+
when :arg
|
557
|
+
emit "lw #{register}, #{arg_reference binding[1]}\n"
|
558
|
+
return register
|
559
|
+
when :local
|
560
|
+
n = binding[1]
|
561
|
+
if register_local? n
|
562
|
+
return local_register(n)
|
563
|
+
else
|
564
|
+
emit "lw #{register}, #{local_reference n}\n"
|
565
|
+
return register
|
566
|
+
end
|
567
|
+
else
|
568
|
+
raise "Don't know how to load #{x.inspect}"
|
569
|
+
end
|
570
|
+
else
|
571
|
+
# Assume global
|
572
|
+
emit "lui #{register}, %hi(#{x})\n"
|
573
|
+
emit "addiu #{register}, %lo(#{x})\n"
|
574
|
+
return register
|
575
|
+
end
|
576
|
+
else
|
577
|
+
raise "Don't know how to load #{x.inspect}"
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
# Load a value into a specific register
|
582
|
+
def load_value_into_register x, register
|
583
|
+
reg = load_value x, register
|
584
|
+
if reg != register
|
585
|
+
emit "move #{register}, #{reg}\n"
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
# Return the offset from $sp at which the nth (0-based) local variable is
|
590
|
+
# stored. For register locals this is the offset at which the saved
|
591
|
+
# value (from the calling function) is stored.
|
592
|
+
def local_offset n
|
593
|
+
@frame_size - @INITIAL_FRAME_SIZE - (n + 1) * @WORDSIZE
|
594
|
+
end
|
595
|
+
|
596
|
+
# Return an $sp-relative reference for the nth (0-based) local
|
597
|
+
def local_reference n
|
598
|
+
"#{local_offset n}($sp)"
|
599
|
+
end
|
600
|
+
|
601
|
+
# Return the register in which the nth local (0-based) is stored, or
|
602
|
+
# nil if not stored in a register
|
603
|
+
def local_register n
|
604
|
+
if register_local? n
|
605
|
+
"$#{@REGISTER_LOCAL_BASE + n}"
|
606
|
+
else
|
607
|
+
nil
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
# Compute the maximum number of locals that would fit in the current
|
612
|
+
# frame size.
|
613
|
+
def max_locals
|
614
|
+
(@frame_size - @INITIAL_FRAME_SIZE) / @WORDSIZE
|
615
|
+
end
|
616
|
+
|
617
|
+
# Calculate the number of stack arguments,
|
618
|
+
# given the total number of arguments.
|
619
|
+
def number_of_stack_arguments n
|
620
|
+
[0, n - @NREGISTER_ARGS].max
|
621
|
+
end
|
622
|
+
|
623
|
+
# Returns true if the nth (0-based) argument is stored in a register
|
624
|
+
def register_arg? n
|
625
|
+
n < @NREGISTER_ARGS
|
626
|
+
end
|
627
|
+
|
628
|
+
# Returns true if the nth (0-based) local is stored in a register
|
629
|
+
def register_local? n
|
630
|
+
n < @NREGISTER_LOCALS
|
133
631
|
end
|
134
632
|
|
135
633
|
# Return a from a function.
|
@@ -138,11 +636,251 @@ module Voodoo
|
|
138
636
|
# of the evaluation is returned from the function.
|
139
637
|
def ret *words
|
140
638
|
emit "# return #{words.join ' '}\n"
|
141
|
-
|
142
|
-
|
143
|
-
|
639
|
+
# Compute return value and store it in @RETURN
|
640
|
+
eval_expr words, @RETURN
|
641
|
+
# Go to epilogue
|
642
|
+
goto @function_end_label
|
144
643
|
end
|
145
|
-
|
146
644
|
|
645
|
+
# Set a variable to the result of evaluating an expression
|
646
|
+
def set symbol, *expr
|
647
|
+
emit "# set #{symbol} #{expr.join ' '}\n"
|
648
|
+
|
649
|
+
x = @environment[symbol]
|
650
|
+
if x == nil
|
651
|
+
raise "Cannot change value of constant #{symbol}"
|
652
|
+
end
|
653
|
+
|
654
|
+
if x[0] == :local
|
655
|
+
register = local_register x[1]
|
656
|
+
else
|
657
|
+
register = nil
|
658
|
+
end
|
659
|
+
|
660
|
+
if register
|
661
|
+
# Set new value
|
662
|
+
eval_expr expr, register
|
663
|
+
else
|
664
|
+
case x[0]
|
665
|
+
when :local
|
666
|
+
ref = local_reference x[1]
|
667
|
+
when :arg
|
668
|
+
ref = arg_reference x[1]
|
669
|
+
else
|
670
|
+
raise "??? #{sym} is neither a local nor an argument"
|
671
|
+
end
|
672
|
+
eval_expr expr, @TEMPORARY
|
673
|
+
emit "sw #{@TEMPORARY}, #{ref}\n"
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
# Set the byte at _base_ + _offset_ to _value_
|
678
|
+
def set_byte base, offset, value
|
679
|
+
emit "# set-byte #{base} #{offset} #{value}\n"
|
680
|
+
# If base is an integer, but offset isn't, swap them
|
681
|
+
if !integer?(offset) && integer?(base)
|
682
|
+
base, offset = [offset, base]
|
683
|
+
end
|
684
|
+
|
685
|
+
value_reg = @TEMPORARIES.pop
|
686
|
+
load_value_into_register value, value_reg
|
687
|
+
begin
|
688
|
+
if integer? offset
|
689
|
+
base_reg = load_value base
|
690
|
+
emit "sb #{value_reg}, #{offset}(#{base_reg})\n"
|
691
|
+
else
|
692
|
+
eval_binop [:add, base, offset], @TEMPORARY
|
693
|
+
emit "sb #{value_reg}, 0(#{@TEMPORARY})\n"
|
694
|
+
end
|
695
|
+
ensure
|
696
|
+
@TEMPORARIES.push value_reg
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
# Set the word at _base_ + _offset_ * +@WORDSIZE+ to _value_
|
701
|
+
def set_word base, offset, value
|
702
|
+
emit "# set-word #{base} #{offset} #{value}\n"
|
703
|
+
|
704
|
+
value_reg = @TEMPORARIES.pop
|
705
|
+
load_value_into_register value, value_reg
|
706
|
+
begin
|
707
|
+
if integer? offset
|
708
|
+
base_reg = load_value base
|
709
|
+
emit "sw #{value_reg}, #{offset * @WORDSIZE}(#{base_reg})\n"
|
710
|
+
else
|
711
|
+
offset_reg = @TEMPORARIES.pop
|
712
|
+
begin
|
713
|
+
load_value_into_register offset, offset_reg
|
714
|
+
base_reg = load_value base
|
715
|
+
emit "sll #{offset_reg}, #{offset_reg}, 2\n"
|
716
|
+
emit "add #{@TEMPORARY}, #{base_reg}, #{offset_reg}\n"
|
717
|
+
emit "sw #{value_reg}, 0(#{@TEMPORARY})\n"
|
718
|
+
ensure
|
719
|
+
@TEMPORARIES.push offset_reg
|
720
|
+
end
|
721
|
+
end
|
722
|
+
ensure
|
723
|
+
@TEMPORARIES.push value_reg
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
# Define a string with the given value
|
728
|
+
def string value
|
729
|
+
code = ''
|
730
|
+
value.each_byte do |b|
|
731
|
+
if b >= 32 && b < 128
|
732
|
+
code << b.chr
|
733
|
+
else
|
734
|
+
code << sprintf("\\%03o", b)
|
735
|
+
end
|
736
|
+
end
|
737
|
+
emit ".ascii \"#{code}\"\n"
|
738
|
+
end
|
739
|
+
|
740
|
+
# Test if a value is a symbol
|
741
|
+
def symbol? value
|
742
|
+
value.kind_of? Symbol
|
743
|
+
end
|
744
|
+
|
745
|
+
# Test if op is a symmetric binary operation (i.e. it will yield the
|
746
|
+
# same result if the order of its source operands is changed).
|
747
|
+
def symmetric_binop? op
|
748
|
+
[:add, :and, :mul, :or, :xor].member? op
|
749
|
+
end
|
750
|
+
|
751
|
+
# Call a function, re-using the current call frame if possible.
|
752
|
+
def tail_call func, *args
|
753
|
+
emit "# tail-call #{func} #{args.join ' '}\n"
|
754
|
+
|
755
|
+
# Compute number of stack arguments
|
756
|
+
nstackargs = number_of_stack_arguments args.length
|
757
|
+
# If we need more stack arguments than we have now,
|
758
|
+
# perform a normal call and return
|
759
|
+
if nstackargs > number_of_stack_arguments(@environment.args)
|
760
|
+
emit "# Not enough space for proper tail call; using regular call\n"
|
761
|
+
ret :call, func, *args
|
762
|
+
end
|
763
|
+
|
764
|
+
# TODO
|
765
|
+
|
766
|
+
# Back up any stack arguments we will need after we overwrite them
|
767
|
+
temporaries = @TEMPORARIES.dup
|
768
|
+
nlocals = @environment.locals
|
769
|
+
(@NREGISTER_ARGS...args.length).each do |i|
|
770
|
+
x = @environment[args[i]]
|
771
|
+
if x && x[0] == :arg && x[1] >= @NREGISTER_ARGS && x[1] < i
|
772
|
+
# args[i] is a stack arg, but has an index < i
|
773
|
+
# That means that, by the time we get to pass arg[i] to the called
|
774
|
+
# function, it will have been overwritten. So we make a backup.
|
775
|
+
if temporaries.empty?
|
776
|
+
# Oh dear, we're out of temporaries.
|
777
|
+
# Store the value in the current stack frame, extending it
|
778
|
+
# as necessary.
|
779
|
+
if nlocals >= max_locals
|
780
|
+
emit "addiu $sp, -8\n"
|
781
|
+
@frame_size = @frame_size + 8
|
782
|
+
end
|
783
|
+
reg = load_value args[i]
|
784
|
+
emit "sw #{reg}, #{local_reference nlocals}\n"
|
785
|
+
args[i] = [:local, nlocals]
|
786
|
+
nlocals = nlocals + 1
|
787
|
+
else
|
788
|
+
# Load the argument into a temporary
|
789
|
+
reg = temporaries.shift
|
790
|
+
load_value_into_register args[i], reg
|
791
|
+
# Update args[i] so we know how to get to it later
|
792
|
+
args[i] = [:reg, reg]
|
793
|
+
end
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
# Set stack arguments
|
798
|
+
(@NREGISTER_ARGS...args.length).each do |i|
|
799
|
+
arg = args[i]
|
800
|
+
reg = @TEMPORARY
|
801
|
+
|
802
|
+
# Load value
|
803
|
+
if arg.kind_of? Array
|
804
|
+
# Special cases, created above
|
805
|
+
case arg[0]
|
806
|
+
when :reg
|
807
|
+
reg = arg[1]
|
808
|
+
when :local
|
809
|
+
emit "lw #{reg}, #{local_reference arg[1]}\n"
|
810
|
+
end
|
811
|
+
else
|
812
|
+
load_value_into_register arg, reg
|
813
|
+
end
|
814
|
+
|
815
|
+
# Store value in designated place
|
816
|
+
emit "sw #{reg}, #{arg_reference i}\n"
|
817
|
+
end
|
818
|
+
|
819
|
+
# Set register arguments
|
820
|
+
[@NREGISTER_ARGS,args.length].min.times do |i|
|
821
|
+
reg = arg_register i
|
822
|
+
load_value_into_register args[i], reg
|
823
|
+
end
|
824
|
+
|
825
|
+
# Restore saved registers
|
826
|
+
[@NREGISTER_LOCALS, @environment.locals].min.times do |n|
|
827
|
+
emit "lw #{local_register n}, #{local_reference n}\n"
|
828
|
+
end
|
829
|
+
# Restore return address
|
830
|
+
emit "lw $31, #{@frame_size - @WORDSIZE}($sp)\n"
|
831
|
+
|
832
|
+
# Adjust stack pointer
|
833
|
+
emit "addiu $sp, #{@frame_size}\n"
|
834
|
+
|
835
|
+
# Load function address
|
836
|
+
if global? func
|
837
|
+
if @imports.has_key? func
|
838
|
+
# Load address from global offset table
|
839
|
+
emit "lw #{@FUNCTION}, %call16(#{func})(#{@GOT})\n"
|
840
|
+
else
|
841
|
+
# Assume label defined in this module
|
842
|
+
emit "lui #{@FUNCTION}, %hi(#{func})\n"
|
843
|
+
emit "addiu #{@FUNCTION}, %lo(#{func})\n"
|
844
|
+
end
|
845
|
+
else
|
846
|
+
# Load address
|
847
|
+
load_value_into_register func, "#{@FUNCTION}"
|
848
|
+
end
|
849
|
+
|
850
|
+
# Perform call
|
851
|
+
emit "jr #{@FUNCTION}\n"
|
852
|
+
# brach delay slot
|
853
|
+
emit "nop\n"
|
854
|
+
end
|
855
|
+
|
856
|
+
# Define a word with the given value
|
857
|
+
def word value
|
858
|
+
emit ".int #{value}\n"
|
859
|
+
end
|
860
|
+
|
861
|
+
# Write generated code to the given IO object.
|
862
|
+
def write io
|
863
|
+
io.puts ".set noat"
|
864
|
+
@sections.each do |section,code|
|
865
|
+
unless code.empty?
|
866
|
+
io.puts ".section #{section.to_s}"
|
867
|
+
io.puts code
|
868
|
+
io.puts
|
869
|
+
end
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
147
873
|
end
|
874
|
+
|
875
|
+
# Register class for big endian MIPS
|
876
|
+
Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
|
877
|
+
:architecture => :mips,
|
878
|
+
:format => :gas
|
879
|
+
|
880
|
+
|
881
|
+
# Register class for little endian MIPS
|
882
|
+
Voodoo::CodeGenerator.register_generator MIPSGasGenerator,
|
883
|
+
:architecture => :mipsel,
|
884
|
+
:format => :gas
|
885
|
+
|
148
886
|
end
|