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.
- data/bin/ast.rb +47 -0
- data/bin/voodooc +146 -0
- data/lib/voodoo.rb +74 -0
- data/lib/voodoo/code_generator.rb +74 -0
- data/lib/voodoo/compiler.rb +45 -0
- data/lib/voodoo/config.rb +43 -0
- data/lib/voodoo/generators/amd64_elf_generator.rb +28 -0
- data/lib/voodoo/generators/amd64_nasm_generator.rb +288 -0
- data/lib/voodoo/generators/command_postprocessor.rb +30 -0
- data/lib/voodoo/generators/common_code_generator.rb +238 -0
- data/lib/voodoo/generators/gas_generator.rb +91 -0
- data/lib/voodoo/generators/generator_api1.rb +95 -0
- data/lib/voodoo/generators/i386_elf_generator.rb +62 -0
- data/lib/voodoo/generators/i386_nasm_generator.rb +177 -0
- data/lib/voodoo/generators/mips_gas_generator.rb +148 -0
- data/lib/voodoo/generators/nasm_elf_generator.rb +55 -0
- data/lib/voodoo/generators/nasm_generator.rb +679 -0
- data/lib/voodoo/parser.rb +283 -0
- metadata +82 -0
@@ -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
|