voodoo 0.5.0

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.
@@ -0,0 +1,91 @@
1
+ require 'voodoo/generators/common_code_generator'
2
+
3
+ module Voodoo
4
+ # Common base class for code generators that target the GNU assembler
5
+ class GasGenerator < CommonCodeGenerator
6
+ def initialize params
7
+ super params
8
+ end
9
+
10
+ #
11
+ # == Alignment
12
+ #
13
+
14
+ # Align data on the next _alignment_-byte boundary.
15
+ # If _alignment_ is not specified, the default data alignment
16
+ # is used.
17
+ def align_data alignment = @DATA_ALIGNMENT
18
+ in_section(:data) { emit ".align #{alignment}" }
19
+ end
20
+
21
+ # Align code on the next _alignment_-byte boundary.
22
+ # If _alignment_ is not specified, the default code alignment
23
+ # is used.
24
+ def align_code alignment = @CODE_ALIGNMENT
25
+ in_section(:code) { emit ".align #{alignment}" }
26
+ end
27
+
28
+ # Align function on the next _alignment_-byte boundary.
29
+ # If _alignment_ is not specified, the default function alignment
30
+ # is used.
31
+ def align_function alignment = @FUNCTION_ALIGNMENT
32
+ in_section(:code) { emit ".align #{alignment}" }
33
+ end
34
+
35
+ #
36
+ # == Data Definition
37
+ #
38
+
39
+ # Define a byte with the given value
40
+ def byte value
41
+ emit ".byte #{value}\n"
42
+ end
43
+
44
+ # Define a string with the given value
45
+ def string value
46
+ code = ''
47
+ value.each_byte do |b|
48
+ if b >= 32 && b < 128
49
+ code << b.chr
50
+ else
51
+ code << sprintf('\%02x', b)
52
+ end
53
+ end
54
+ emit ".ascii \"#{code}\"\n"
55
+ end
56
+
57
+ # Define a machine word with the given value
58
+ def word value
59
+ emit ".word #{value}\n"
60
+ end
61
+
62
+ #
63
+ # == Information About the Generator
64
+ #
65
+
66
+ # Returns the number of bits per word for this code generator.
67
+ def wordsize
68
+ @WORDSIZE * 8
69
+ end
70
+
71
+ #
72
+ # == Labels
73
+ #
74
+
75
+ # Export symbols from the current section
76
+ def export *symbols
77
+ symbols.each { |sym| emit ".globl #{sym}\n" }
78
+ end
79
+
80
+ # Import labels into the current section
81
+ def import *symbols
82
+ # nothing
83
+ end
84
+
85
+ # Define a label in the current section
86
+ def label name
87
+ emit "#{name}:\n"
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,95 @@
1
+ module Voodoo
2
+ # This module implements the old code generation API, consisting of
3
+ # the following methods:
4
+ #
5
+ # #add_code
6
+ # #add_code_label
7
+ # #add_data
8
+ # #add_data_label
9
+ # #add_function
10
+ # #add_function_label
11
+ # #align_code
12
+ # #align_data
13
+ # #align_function
14
+ #
15
+ module GeneratorApi1
16
+ # Add code.
17
+ #
18
+ # Parameters:
19
+ # [actions] an Array of actions to be added
20
+ #
21
+ # Example:
22
+ # add_code [:call, :exit, 0]
23
+ def add_code *actions
24
+ add :code, *actions
25
+ end
26
+
27
+ # Add a label at the current address in the code section.
28
+ #
29
+ # Example:
30
+ # add_code_label :mylabel
31
+ def add_code_label name
32
+ in_section(:code) { label name }
33
+ end
34
+
35
+ # Add data.
36
+ #
37
+ # Parameters:
38
+ # [defs] an Array of data definitions
39
+ #
40
+ # Example:
41
+ # add_data [:word, 42]
42
+ def add_data *defs
43
+ add :data, *defs
44
+ end
45
+
46
+ # Add a label at the current address in the data section.
47
+ #
48
+ # Example:
49
+ # add_data_label :msg
50
+ def add_data_label name
51
+ add :data, [:label, name]
52
+ end
53
+
54
+ # Add a label at the address where the next function will be generated.
55
+ #
56
+ # Example:
57
+ # add_function_label :add_one
58
+ def add_function_label name
59
+ add :functions, [:label, name]
60
+ end
61
+
62
+ # Align code on the next _alignment_-byte boundary.
63
+ # If _alignment_ is not specified, the default code alignment
64
+ # is used.
65
+ def align_code alignment = nil
66
+ if alignment
67
+ add :code, [:align, alignment]
68
+ else
69
+ add :code, [:align]
70
+ end
71
+ end
72
+
73
+ # Align data on the next _alignment_-byte boundary.
74
+ # If _alignment_ is not specified, the default data alignment
75
+ # is used.
76
+ def align_data alignment = nil
77
+ if alignment
78
+ add :data, [:align, alignment]
79
+ else
80
+ add :data, [:align]
81
+ end
82
+ end
83
+
84
+ # Align function on the next _alignment_-byte boundary.
85
+ # If _alignment_ is not specified, the default function alignment
86
+ # is used.
87
+ def align_function alignment = nil
88
+ if alignment
89
+ add :functions, [:align, alignment]
90
+ else
91
+ add :functions, [:align]
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,62 @@
1
+ require 'delegate'
2
+ require 'tempfile'
3
+ require 'voodoo/config'
4
+ require 'voodoo/generators/i386_nasm_generator'
5
+
6
+ module Voodoo
7
+ # Generator that produces ELF objects
8
+ class I386ELFGenerator < DelegateClass(I386NasmGenerator)
9
+ def initialize params = {}
10
+ @nasmgenerator = I386NasmGenerator.new params
11
+ super(@nasmgenerator)
12
+ end
13
+
14
+ def output_file_name input_name
15
+ input_name.sub(/\.voo$/, '') + '.o'
16
+ end
17
+
18
+ # Encodes a string so that it is safe for use as a shell argument
19
+ def shell_encode string
20
+ '"' + string.gsub(/([\\`"$\n])/, "\\\\\\1") + '"'
21
+ end
22
+
23
+ # Writes the generated code to the given IO handle
24
+ def write io
25
+ # Create temporary file to write NASM code to
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
55
+ end
56
+ end
57
+
58
+ # Register class
59
+ Voodoo::CodeGenerator.register_generator I386ELFGenerator,
60
+ :architecture => :i386,
61
+ :format => :elf
62
+ end
@@ -0,0 +1,177 @@
1
+ require 'voodoo/generators/common_code_generator'
2
+
3
+ module Voodoo
4
+ # = i386 NASM Code Generator
5
+ #
6
+ # The i386 NASM code generator generates i386 assembly code for use with
7
+ # the {Netwide Assembler}[http://www.nasm.us/].
8
+ #
9
+ # == Calling Convention
10
+ #
11
+ # Function arguments are pushed on the stack in reverse order, so that
12
+ # the first argument is pushed last. Each argument occupies one word
13
+ # of stack space. These arguments are removed from the stack by the
14
+ # caller after the called function returns.
15
+ #
16
+ # The return value is passed in +eax+.
17
+ #
18
+ # == Call Frames
19
+ #
20
+ # Call frames have the following layout:
21
+ #
22
+ # argn
23
+ # :
24
+ # arg1
25
+ # arg0 <-- ebp + 8
26
+ # oldeip <-- ebp + 4
27
+ # oldebp <-- ebp
28
+ # local0 <-- ebp - 4
29
+ # local1 <-- ebp - 8
30
+ # :
31
+ # localn <-- esp
32
+ #
33
+ class I386NasmGenerator < NasmGenerator
34
+ WORDSIZE = 4
35
+
36
+ def initialize params = {}
37
+ # Number of bytes in a word
38
+ @WORDSIZE = 4
39
+ # Word name in NASM lingo
40
+ @WORD_NAME = 'dword'
41
+ # Default alignment for code
42
+ @CODE_ALIGNMENT = 0
43
+ # Default alignment for data
44
+ @DATA_ALIGNMENT = @WORDSIZE
45
+ # Default alignment for functions
46
+ @FUNCTION_ALIGNMENT = 16
47
+ # Register used for return values
48
+ @RETURN_REG = 'eax'
49
+ # Register used as scratch register
50
+ @SCRATCH_REG = 'ebx'
51
+ # Accumulator index
52
+ @AX = 'eax'
53
+ # Base index
54
+ @BX = 'ebx'
55
+ # Count index
56
+ @CX = 'ecx'
57
+ # Data index
58
+ @DX = 'edx'
59
+ super params
60
+ end
61
+
62
+ # Call a function
63
+ def call func, *args
64
+ emit "; call #{func} #{args.join ' '}\n"
65
+ revargs = args.reverse
66
+ revargs.each { |arg| push arg }
67
+ use_value "call", func
68
+ if args.length > 0
69
+ emit "add esp, #{WORDSIZE * args.length}\n"
70
+ end
71
+ end
72
+
73
+ # Emit function prologue.
74
+ def emit_function_prologue formals = []
75
+ emit "push ebp\nmov ebp, esp\n"
76
+ end
77
+
78
+ # Load the value of the nth argument
79
+ def load_arg n, reg = @SCRATCH_REG
80
+ "[ebp + #{n * @WORDSIZE + 8}]"
81
+ end
82
+
83
+ # Load the value of the nth local variable
84
+ def load_local n, reg = @SCRATCH_REG
85
+ "[ebp - #{(n + 1) * @WORDSIZE}]"
86
+ end
87
+
88
+ # Introduce a new local variable
89
+ def let symbol, *words
90
+ emit "; let #{symbol} #{words.join ' '}\n"
91
+ @environment.add_local symbol
92
+ eval_expr words
93
+ emit "push eax\n"
94
+ end
95
+
96
+ # Push a word on the stack
97
+ def push value
98
+ #emit "; push #{value}\n"
99
+ value_ref = load_value value, "ebx"
100
+ emit "push dword #{value_ref}\n"
101
+ end
102
+
103
+ # Call a function, re-using the current call fram if possible
104
+ def tail_call fun, *args
105
+ emit "; tail-call #{fun} #{args.join ' '}\n"
106
+ if args.length > @environment.args
107
+ # Not enough space to do proper tail call; do normal call instead
108
+ emit "; not enough space for proper tail call; changed to regular call\n"
109
+ ret :call, fun, *args
110
+ else
111
+ # Any value in the current frame that is passed to the called
112
+ # function must be copied to a local variable if it would otherwise
113
+ # be overwritten before it is used
114
+ i = args.length - 1
115
+ while i >= -1
116
+ arg = (i >= 0) ? args[i] : fun
117
+
118
+ if symbol?(arg)
119
+ x = @environment[arg]
120
+ if x && x[0] == :arg && x[1] < args.length && x[1] > i &&
121
+ (i >= 0 || fun != args[x[1]])
122
+ # Save value
123
+ newsym = @environment.gensym
124
+ let newsym, arg
125
+ # Change reference
126
+ if i >= 0
127
+ args[i] = newsym
128
+ else
129
+ fun = newsym
130
+ end
131
+ end
132
+ end
133
+ i = i - 1
134
+ end
135
+
136
+ # Set arguments
137
+ if args.length > 0
138
+ (args.length - 1 .. 0).each do |i|
139
+ arg = args[i]
140
+
141
+ value_ref = load_value arg, "eax"
142
+ newarg_ref = "[ebp + #{(i + 2) * WORDSIZE}]"
143
+ # Elide code if source is same as destination
144
+ unless value_ref == newarg_ref
145
+ if memory_operand?(value_ref)
146
+ emit "mov eax, #{value_ref}\n"
147
+ value_ref = "eax"
148
+ end
149
+ emit "mov #{@WORD_NAME} [ebp + #{(i + 2) * WORDSIZE}], " +
150
+ "#{value_ref}\n"
151
+ end
152
+ end
153
+ end
154
+
155
+ # Tail call
156
+ emit "mov esp, ebp\npop ebp\n"
157
+ use_value "jmp", fun
158
+ end
159
+ end
160
+
161
+ def use_value operation, value
162
+ value_ref = load_value value, "eax"
163
+ emit "#{operation} #{value_ref}\n"
164
+ end
165
+
166
+ # Define a machine word with the given value
167
+ def word value
168
+ emit "dd #{value}\n"
169
+ end
170
+
171
+ end
172
+
173
+ # Register class
174
+ Voodoo::CodeGenerator.register_generator I386NasmGenerator,
175
+ :architecture => :i386,
176
+ :format => :nasm
177
+ end
@@ -0,0 +1,148 @@
1
+ require 'voodoo/generators/gas_generator'
2
+
3
+ module Voodoo
4
+ # = MIPS GNU Assembler Code Generator
5
+ #
6
+ # The MIPS code generator generates i386 assembly code for use with
7
+ # the GNU assembler.
8
+ #
9
+ # == Calling Convention
10
+ #
11
+ # The first four arguments are passed in the registers $4 through $7.
12
+ # Any additional arguments are passed in registers.
13
+ #
14
+ # The return value is passed in $2.
15
+ #
16
+ # This calling convention is compatible with the System V ELF ABI.
17
+ #
18
+ # == Call Frames
19
+ #
20
+ # Call frames have the following layout:
21
+ #
22
+ # When a function is called, it receives a stack frame that looks like
23
+ # the following:
24
+ #
25
+ # :
26
+ # old frame
27
+ # argn
28
+ # :
29
+ # arg4
30
+ # empty3
31
+ # empty2
32
+ # empty1
33
+ # empty0 <-- $sp points here
34
+ #
35
+ # Where $sp is $29, and must be a multiple of 8.
36
+ #
37
+ # TODO: Description of how we organize things below the original frame.
38
+ # FIXME: Following is just a sketch and is likely neither complete nor
39
+ # correct.
40
+ # See the ELF ABI for MIPS, Standard Called Function Rules.
41
+ #
42
+ # 1. Adjust $sp to create new stack frame
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
49
+ #
50
+ # Note that, to comply with the standard called function rules laid out
51
+ # in the System V ABI, a function can only have one exit. Thus, early
52
+ # returns will have to be translated to gotos to the single exit.
53
+ #
54
+ # saved_r
55
+ # saved_sp
56
+ # :
57
+ # local8
58
+ # :
59
+ # localn <-- $sp points here
60
+ #
61
+ # Register $0 is always 0.
62
+ #
63
+ # Registers $8 through $15 and register $24 are used as temporaries.
64
+ #
65
+ # Registers $16 through $23 and register $30 are used for local
66
+ # variables. They are callee-save, which means the value they had upon
67
+ # function entry must be restored before returning.
68
+ #
69
+ # Register $25 is used to hold the address of the called function when
70
+ # position-independent code is called. Otherwise, it may be used as
71
+ # a temporary.
72
+ #
73
+ # Registers $26 and $27 are reserved for operating system use.
74
+ #
75
+ # Register $28 is used as global pointer (FIXME: what does this mean?)
76
+ #
77
+ # Register $29 is used as stack pointer. It is callee-save: the
78
+ # value it has upon function entry must be restored before returning.
79
+ # The stack pointer must always be a multiple of 8.
80
+ #
81
+ # Register $31 is used to store the return address when calling a function.
82
+ #
83
+ module MIPSGasGenerator
84
+ def initialize params
85
+ @WORDSIZE = 4
86
+ @CODE_ALIGNMENT = 0
87
+ @DATA_ALIGNMENT = @WORDSIZE
88
+ @FUNCTION_ALIGNMENT = @WORDSIZE
89
+ super params
90
+ end
91
+
92
+ #
93
+ # == Functions
94
+ #
95
+
96
+ # Emit function prologue and declare _formals_ as function arguments
97
+ def begin_function *formals
98
+ emit "# function #{formals.join ' '}\n"
99
+ environment = Environment.new @environment
100
+ environment.add_args formals
101
+ @environment = environment
102
+ emit_function_prologue formals
103
+ end
104
+
105
+ # Call a function.
106
+ def call func, *args
107
+ raise 'TODO'
108
+ end
109
+
110
+ # Emit function epilogue.
111
+ def emit_function_epilogue formals = []
112
+ raise 'TODO'
113
+ end
114
+
115
+ # Emit function prologue.
116
+ def emit_function_prologue formals = []
117
+ raise 'TODO'
118
+ end
119
+
120
+ # End a function body
121
+ def end_function
122
+ emit "# end function\n\n"
123
+ if @environment == @top_level
124
+ raise "Cannot end function when not in a function"
125
+ else
126
+ @environment = @top_level
127
+ end
128
+ end
129
+
130
+ # Call a function, re-using the current call frame if possible.
131
+ def tail_call func, *args
132
+ raise 'TODO'
133
+ end
134
+
135
+ # Return a from a function.
136
+ #
137
+ # _words_ may contain an expression to be evaluated. The result
138
+ # of the evaluation is returned from the function.
139
+ def ret *words
140
+ emit "# return #{words.join ' '}\n"
141
+ eval_expr words
142
+ emit_function_epilogue
143
+ emit "j $31\n"
144
+ end
145
+
146
+
147
+ end
148
+ end