metasm 1.0.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/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- metadata +255 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'metasm/arm/opcodes'
|
|
8
|
+
require 'metasm/parse'
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
class ARM
|
|
12
|
+
def opcode_list_byname
|
|
13
|
+
@opcode_list_byname ||= opcode_list.inject({}) { |h, o|
|
|
14
|
+
(h[o.name] ||= []) << o
|
|
15
|
+
if o.props[:cond]
|
|
16
|
+
coff = o.props[:cond_name_off] || o.name.length
|
|
17
|
+
%w[eq ne cs cc mi pl vs vc hi ls ge lt gt le al].each { |cd|
|
|
18
|
+
n = o.name.dup
|
|
19
|
+
n[coff, 0] = cd
|
|
20
|
+
(h[n] ||= []) << o
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
h
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def parse_arg_valid?(op, sym, arg)
|
|
28
|
+
case sym
|
|
29
|
+
when :rd, :rs, :rn, :rm; arg.kind_of? Reg and arg.shift == 0 and (arg.updated ? op.props[:baseincr] : !op.props[:baseincr])
|
|
30
|
+
when :rm_rs; arg.kind_of? Reg and arg.shift.kind_of? Reg
|
|
31
|
+
when :rm_is; arg.kind_of? Reg and arg.shift.kind_of? Integer
|
|
32
|
+
when :i16, :i24, :i8_12, :i8_r; arg.kind_of? Expression
|
|
33
|
+
when :mem_rn_rm, :mem_rn_i8_12, :mem_rn_rms, :mem_rn_i12
|
|
34
|
+
os = case sym
|
|
35
|
+
when :mem_rn_rm; :rm
|
|
36
|
+
when :mem_rn_i8_12; :i8_12
|
|
37
|
+
when :mem_rn_rms; :rm_rs
|
|
38
|
+
when :mem_rn_i12; :i16
|
|
39
|
+
end
|
|
40
|
+
arg.kind_of? Memref and parse_arg_valid?(op, os, arg.offset)
|
|
41
|
+
when :reglist; arg.kind_of? RegList
|
|
42
|
+
end
|
|
43
|
+
# TODO check flags on reglist, check int values
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def parse_argument(lexer)
|
|
47
|
+
if Reg.s_to_i[lexer.nexttok.raw]
|
|
48
|
+
arg = Reg.new Reg.s_to_i[lexer.readtok.raw]
|
|
49
|
+
lexer.skip_space
|
|
50
|
+
case lexer.nexttok.raw.downcase
|
|
51
|
+
when 'lsl', 'lsr', 'asr', 'ror'
|
|
52
|
+
arg.stype = lexer.readtok.raw.downcase.to_sym
|
|
53
|
+
lexer.skip_space
|
|
54
|
+
if Reg.s_to_i[lexer.nexttok.raw]
|
|
55
|
+
arg.shift = Reg.new Reg.s_to_i[lexer.readtok.raw]
|
|
56
|
+
else
|
|
57
|
+
arg.shift = Expression.parse(lexer).reduce
|
|
58
|
+
end
|
|
59
|
+
when 'rrx'
|
|
60
|
+
lexer.readtok
|
|
61
|
+
arg.stype = :ror
|
|
62
|
+
when '!'
|
|
63
|
+
lexer.readtok
|
|
64
|
+
arg.updated = true
|
|
65
|
+
end
|
|
66
|
+
elsif lexer.nexttok.raw == '{'
|
|
67
|
+
lexer.readtok
|
|
68
|
+
arg = RegList.new
|
|
69
|
+
loop do
|
|
70
|
+
raise "unterminated reglist" if lexer.eos?
|
|
71
|
+
lexer.skip_space
|
|
72
|
+
if Reg.s_to_i[lexer.nexttok.raw]
|
|
73
|
+
arg.list << Reg.new(Reg.s_to_i[lexer.readtok.raw])
|
|
74
|
+
lexer.skip_space
|
|
75
|
+
end
|
|
76
|
+
case lexer.nexttok.raw
|
|
77
|
+
when ','; lexer.readtok
|
|
78
|
+
when '-'
|
|
79
|
+
lexer.readtok
|
|
80
|
+
lexer.skip_space
|
|
81
|
+
if not r = Reg.s_to_i[lexer.nexttok.raw]
|
|
82
|
+
raise lexer, "reglist parse error: invalid range"
|
|
83
|
+
end
|
|
84
|
+
lexer.readtok
|
|
85
|
+
(arg.list.last.i+1..r).each { |v|
|
|
86
|
+
arg.list << Reg.new(v)
|
|
87
|
+
}
|
|
88
|
+
when '}'; lexer.readtok ; break
|
|
89
|
+
else raise lexer, "reglist parse error: ',' or '}' expected, got #{lexer.nexttok.raw.inspect}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
if lexer.nexttok and lexer.nexttok.raw == '^'
|
|
93
|
+
lexer.readtok
|
|
94
|
+
arg.usermoderegs = true
|
|
95
|
+
end
|
|
96
|
+
elsif lexer.nexttok.raw == '['
|
|
97
|
+
lexer.readtok
|
|
98
|
+
if not base = Reg.s_to_i[lexer.nexttok.raw]
|
|
99
|
+
raise lexer, 'invalid mem base (reg expected)'
|
|
100
|
+
end
|
|
101
|
+
base = Reg.new Reg.s_to_i[lexer.readtok.raw]
|
|
102
|
+
if lexer.nexttok.raw == ']'
|
|
103
|
+
lexer.readtok
|
|
104
|
+
closed = true
|
|
105
|
+
end
|
|
106
|
+
if lexer.nexttok.raw != ','
|
|
107
|
+
raise lexer, 'mem off expected'
|
|
108
|
+
end
|
|
109
|
+
lexer.readtok
|
|
110
|
+
off = parse_argument(lexer)
|
|
111
|
+
if not off.kind_of? Expression and not off.kind_of? Reg
|
|
112
|
+
raise lexer, 'invalid mem off (reg/imm expected)'
|
|
113
|
+
end
|
|
114
|
+
case lexer.nexttok and lexer.nexttok.raw
|
|
115
|
+
when ']'
|
|
116
|
+
when ','
|
|
117
|
+
end
|
|
118
|
+
lexer.readtok
|
|
119
|
+
arg = Memref.new(base, off)
|
|
120
|
+
if lexer.nexttok and lexer.nexttok.raw == '!'
|
|
121
|
+
lexer.readtok
|
|
122
|
+
arg.incr = :pre # TODO :post
|
|
123
|
+
end
|
|
124
|
+
else
|
|
125
|
+
arg = Expression.parse lexer
|
|
126
|
+
end
|
|
127
|
+
arg
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
require 'metasm/render'
|
|
7
|
+
require 'metasm/arm/opcodes'
|
|
8
|
+
|
|
9
|
+
module Metasm
|
|
10
|
+
class ARM
|
|
11
|
+
class Reg
|
|
12
|
+
include Renderable
|
|
13
|
+
def render
|
|
14
|
+
r = self.class.i_to_s[@i]
|
|
15
|
+
r += '!' if updated
|
|
16
|
+
if @stype == :lsl and @shift == 0
|
|
17
|
+
[r]
|
|
18
|
+
elsif @stype == :ror and @shift == 0
|
|
19
|
+
["#{r} RRX"]
|
|
20
|
+
else
|
|
21
|
+
case s = @shift
|
|
22
|
+
when Integer; s = Expression[s]
|
|
23
|
+
when Reg; s = self.class.i_to_s[s.i]
|
|
24
|
+
end
|
|
25
|
+
["#{r} #{@stype.to_s.upcase} #{s}"]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class Memref
|
|
31
|
+
include Renderable
|
|
32
|
+
def render
|
|
33
|
+
o = @offset
|
|
34
|
+
o = Expression[o] if o.kind_of? Integer
|
|
35
|
+
case @incr
|
|
36
|
+
when nil; ['[', @base, ', ', o, ']']
|
|
37
|
+
when :pre; ['[', @base, ', ', o, ']!']
|
|
38
|
+
when :post; ['[', @base, '], ', o]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class RegList
|
|
44
|
+
include Renderable
|
|
45
|
+
def render
|
|
46
|
+
r = ['{']
|
|
47
|
+
@list.each { |l| r << l << ', ' }
|
|
48
|
+
r[-1] = '}'
|
|
49
|
+
r << '^' if usermoderegs
|
|
50
|
+
r
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
@@ -0,0 +1,1457 @@
|
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
|
2
|
+
# Copyright (C) 2006-2009 Yoann GUILLOT
|
|
3
|
+
#
|
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
require 'metasm/main'
|
|
8
|
+
require 'metasm/parse_c'
|
|
9
|
+
|
|
10
|
+
module Metasm
|
|
11
|
+
module C
|
|
12
|
+
class Parser
|
|
13
|
+
def precompile
|
|
14
|
+
@toplevel.precompile(Compiler.new(self))
|
|
15
|
+
self
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# each CPU defines a subclass of this one
|
|
20
|
+
class Compiler
|
|
21
|
+
# an ExeFormat (mostly used for unique label creation)
|
|
22
|
+
attr_accessor :exeformat
|
|
23
|
+
# the C Parser (destroyed by compilation)
|
|
24
|
+
attr_accessor :parser
|
|
25
|
+
# an array of assembler statements (strings)
|
|
26
|
+
attr_accessor :source
|
|
27
|
+
# list of unique labels generated (to recognize user-defined ones)
|
|
28
|
+
attr_accessor :auto_label_list
|
|
29
|
+
# map asm name -> original C name (for exports etc)
|
|
30
|
+
attr_accessor :label_oldname
|
|
31
|
+
|
|
32
|
+
attr_accessor :curexpr
|
|
33
|
+
# allows 'raise self' (eg struct.offsetof)
|
|
34
|
+
def exception(msg='EOF unexpected')
|
|
35
|
+
ParseError.new "near #@curexpr: #{msg}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# creates a new CCompiler from an ExeFormat and a C Parser
|
|
39
|
+
def initialize(parser, exeformat=ExeFormat.new, source=[])
|
|
40
|
+
@parser, @exeformat, @source = parser, exeformat, source
|
|
41
|
+
@auto_label_list = {}
|
|
42
|
+
@label_oldname = {}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def new_label(base='')
|
|
46
|
+
lbl = @exeformat.new_label base
|
|
47
|
+
@auto_label_list[lbl] = true
|
|
48
|
+
lbl
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def toplevel ; @parser.toplevel end
|
|
52
|
+
def typesize ; @parser.typesize end
|
|
53
|
+
def sizeof(*a) @parser.sizeof(*a) end
|
|
54
|
+
|
|
55
|
+
# compiles the c parser toplevel to assembler statements in self.source (::Array of ::String)
|
|
56
|
+
#
|
|
57
|
+
# starts by precompiling parser.toplevel (destructively):
|
|
58
|
+
# static symbols are converted to toplevel ones, as nested functions
|
|
59
|
+
# uses an ExeFormat (the argument) to create unique label/variable names
|
|
60
|
+
#
|
|
61
|
+
# remove typedefs/enums
|
|
62
|
+
# CExpressions: all expr types are converted to __int8/__int16/__int32/__int64 (sign kept) (incl. ptr), + void
|
|
63
|
+
# struct member dereference/array indexes are converted to *(ptr + off)
|
|
64
|
+
# coma are converted to 2 statements, ?: are converted to If
|
|
65
|
+
# :|| and :&& are converted to If + assignment to temporary
|
|
66
|
+
# immediate quotedstrings/floats are converted to references to const static toplevel
|
|
67
|
+
# postincrements are replaced by a temporary (XXX arglist)
|
|
68
|
+
# compound statements are unnested
|
|
69
|
+
# Asm are kept (TODO precompile clobber types)
|
|
70
|
+
# Declarations: initializers are converted to separate assignment CExpressions
|
|
71
|
+
# Blocks are kept unless empty
|
|
72
|
+
# structure dereferences/array indexing are converted to *(ptr + offset)
|
|
73
|
+
# While/For/DoWhile/Switch are converted to If/Goto
|
|
74
|
+
# Continue/Break are converted to Goto
|
|
75
|
+
# Cases are converted to Labels during Switch conversion
|
|
76
|
+
# Label statements are removed
|
|
77
|
+
# Return: 'return <foo>;' => 'return <foo>; goto <end_of_func>;', 'return;' => 'goto <eof>;'
|
|
78
|
+
# If: 'if (a) b; else c;' => 'if (a) goto l1; { c; }; goto l2; l1: { b; } l2:'
|
|
79
|
+
# && and || in condition are expanded to multiple If
|
|
80
|
+
# functions returning struct are precompiled (in Declaration/CExpression/Return)
|
|
81
|
+
#
|
|
82
|
+
# in a second phase, unused labels are removed from functions, as noop goto (goto x; x:)
|
|
83
|
+
# dead code is removed ('goto foo; bar; baz:' => 'goto foo; baz:') (TODO)
|
|
84
|
+
#
|
|
85
|
+
# after that, toplevel is no longer valid C (bad types, blocks moved...)
|
|
86
|
+
#
|
|
87
|
+
# then toplevel statements are sorted (.text, .data, .rodata, .bss) and compiled into asm statements in self.source
|
|
88
|
+
#
|
|
89
|
+
# returns the asm source in a single string
|
|
90
|
+
def compile
|
|
91
|
+
cf = @exeformat.unique_labels_cache.keys & @auto_label_list.keys
|
|
92
|
+
raise "compile_c name conflict: #{cf.inspect}" if not cf.empty?
|
|
93
|
+
@exeformat.unique_labels_cache.update @auto_label_list
|
|
94
|
+
|
|
95
|
+
@parser.toplevel.precompile(self)
|
|
96
|
+
|
|
97
|
+
# reorder statements (arrays of Variables) following exe section typical order
|
|
98
|
+
funcs, rwdata, rodata, udata = [], [], [], []
|
|
99
|
+
@parser.toplevel.statements.each { |st|
|
|
100
|
+
if st.kind_of? Asm
|
|
101
|
+
@source << st.body
|
|
102
|
+
next
|
|
103
|
+
end
|
|
104
|
+
raise 'non-declaration at toplevel! ' + st.inspect if not st.kind_of? Declaration
|
|
105
|
+
v = st.var
|
|
106
|
+
if v.type.kind_of? Function
|
|
107
|
+
funcs << v if v.initializer # no initializer == storage :extern
|
|
108
|
+
elsif v.storage == :extern
|
|
109
|
+
elsif v.initializer
|
|
110
|
+
if v.type.qualifier.to_a.include?(:const) or
|
|
111
|
+
(v.type.kind_of? Array and v.type.type.qualifier.to_a.include?(:const))
|
|
112
|
+
rodata << v
|
|
113
|
+
else
|
|
114
|
+
rwdata << v
|
|
115
|
+
end
|
|
116
|
+
else
|
|
117
|
+
udata << v
|
|
118
|
+
end
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if not funcs.empty?
|
|
122
|
+
@exeformat.compile_setsection @source, '.text'
|
|
123
|
+
funcs.each { |func| c_function(func) }
|
|
124
|
+
c_program_epilog
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
align = 1
|
|
128
|
+
if not rwdata.empty?
|
|
129
|
+
@exeformat.compile_setsection @source, '.data'
|
|
130
|
+
rwdata.each { |data| align = c_idata(data, align) }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if not rodata.empty?
|
|
134
|
+
@exeformat.compile_setsection @source, '.rodata'
|
|
135
|
+
rodata.each { |data| align = c_idata(data, align) }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
if not udata.empty?
|
|
139
|
+
@exeformat.compile_setsection @source, '.bss'
|
|
140
|
+
udata.each { |data| align = c_udata(data, align) }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# needed to allow asm parser to use our autogenerated label names
|
|
144
|
+
@exeformat.unique_labels_cache.delete_if { |k, v| @auto_label_list[k] }
|
|
145
|
+
|
|
146
|
+
@source.join("\n")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# compiles a C function +func+ to asm source into the array of strings +str+
|
|
150
|
+
# in a first pass the stack variable offsets are computed,
|
|
151
|
+
# then each statement is compiled in turn
|
|
152
|
+
def c_function(func)
|
|
153
|
+
# must wait the Declaration to run the CExpr for dynamic auto offsets,
|
|
154
|
+
# and must run those statements once only
|
|
155
|
+
# TODO alloc a stack variable to maintain the size for each dynamic array
|
|
156
|
+
# TODO offset of arguments
|
|
157
|
+
# TODO nested function
|
|
158
|
+
c_init_state(func)
|
|
159
|
+
|
|
160
|
+
# hide the full @source while compiling, then add prolog/epilog (saves 1 pass)
|
|
161
|
+
@source << ''
|
|
162
|
+
@source << "#{@label_oldname[func.name]}:" if @label_oldname[func.name]
|
|
163
|
+
@source << "#{func.name}:"
|
|
164
|
+
presource, @source = @source, []
|
|
165
|
+
|
|
166
|
+
c_block(func.initializer)
|
|
167
|
+
|
|
168
|
+
tmpsource, @source = @source, presource
|
|
169
|
+
c_prolog
|
|
170
|
+
@source.concat tmpsource
|
|
171
|
+
c_epilog
|
|
172
|
+
@source << ''
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def c_block(blk)
|
|
176
|
+
c_block_enter(blk)
|
|
177
|
+
blk.statements.each { |stmt|
|
|
178
|
+
case stmt
|
|
179
|
+
when CExpression; c_cexpr(stmt)
|
|
180
|
+
when Declaration; c_decl(stmt.var)
|
|
181
|
+
when If; c_ifgoto(stmt.test, stmt.bthen.target)
|
|
182
|
+
when Goto; c_goto(stmt.target)
|
|
183
|
+
when Label; c_label(stmt.name)
|
|
184
|
+
when Return; c_return(stmt.value)
|
|
185
|
+
when Asm; c_asm(stmt)
|
|
186
|
+
when Block; c_block(stmt)
|
|
187
|
+
else raise
|
|
188
|
+
end
|
|
189
|
+
}
|
|
190
|
+
c_block_exit(blk)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def c_block_enter(blk)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def c_block_exit(blk)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def c_label(name)
|
|
200
|
+
@source << "#{name}:"
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# fills @state.offset (empty hash)
|
|
204
|
+
# automatic variable => stack offset, (recursive)
|
|
205
|
+
# offset is an ::Integer or a CExpression (dynamic array)
|
|
206
|
+
# assumes offset 0 is a ptr-size-aligned address
|
|
207
|
+
# TODO registerize automatic variables
|
|
208
|
+
def c_reserve_stack(block, off = 0)
|
|
209
|
+
block.statements.each { |stmt|
|
|
210
|
+
case stmt
|
|
211
|
+
when Declaration
|
|
212
|
+
next if stmt.var.type.kind_of? Function
|
|
213
|
+
off = c_reserve_stack_var(stmt.var, off)
|
|
214
|
+
@state.offset[stmt.var] = off
|
|
215
|
+
when Block
|
|
216
|
+
c_reserve_stack(stmt, off)
|
|
217
|
+
# do not update off, not nested subblocks can overlap
|
|
218
|
+
end
|
|
219
|
+
}
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# computes the new stack offset for var
|
|
223
|
+
# off is either an offset from stack start (:ptr-size-aligned) or
|
|
224
|
+
# a CExpression [[[expr, +, 7], &, -7], +, off]
|
|
225
|
+
def c_reserve_stack_var(var, off)
|
|
226
|
+
if (arr_type = var.type).kind_of? Array and (arr_sz = arr_type.length).kind_of? CExpression
|
|
227
|
+
# dynamic array !
|
|
228
|
+
arr_sz = CExpression.new(arr_sz, :*, sizeof(nil, arr_type.type),
|
|
229
|
+
BaseType.new(:long, :unsigned)).precompile_inner(@parser, nil)
|
|
230
|
+
off = CExpression.new(arr_sz, :+, off, arr_sz.type)
|
|
231
|
+
off = CExpression.new(off, :+, 7, off.type)
|
|
232
|
+
off = CExpression.new(off, :&, -7, off.type)
|
|
233
|
+
CExpression.new(off, :+, 0, off.type)
|
|
234
|
+
else
|
|
235
|
+
al = var.type.align(@parser)
|
|
236
|
+
sz = sizeof(var)
|
|
237
|
+
case off
|
|
238
|
+
when CExpression; CExpression.new(off.lexpr, :+, ((off.rexpr + sz + al - 1) / al * al), off.type)
|
|
239
|
+
else (off + sz + al - 1) / al * al
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# here you can add thing like stubs for PIC code
|
|
245
|
+
def c_program_epilog
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# compiles a C static data definition into an asm string
|
|
249
|
+
# returns the new alignment value
|
|
250
|
+
def c_idata(data, align)
|
|
251
|
+
w = data.type.align(@parser)
|
|
252
|
+
@source << ".align #{align = w}" if w > align
|
|
253
|
+
|
|
254
|
+
@source << "#{@label_oldname[data.name]}:" if @label_oldname[data.name]
|
|
255
|
+
@source << data.name.dup
|
|
256
|
+
len = c_idata_inner(data.type, data.initializer)
|
|
257
|
+
len %= w
|
|
258
|
+
len == 0 ? w : len
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# dumps an anonymous variable definition, appending to the last line of source
|
|
262
|
+
# source.last is a label name or is empty before calling here
|
|
263
|
+
# return the length of the data written
|
|
264
|
+
def c_idata_inner(type, value)
|
|
265
|
+
case type
|
|
266
|
+
when BaseType
|
|
267
|
+
value ||= 0
|
|
268
|
+
|
|
269
|
+
if type.name == :void
|
|
270
|
+
@source.last << ':' if not @source.last.empty?
|
|
271
|
+
return 0
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
@source.last <<
|
|
275
|
+
case type.name
|
|
276
|
+
when :__int8; ' db '
|
|
277
|
+
when :__int16; ' dw '
|
|
278
|
+
when :__int32; ' dd '
|
|
279
|
+
when :__int64; ' dq '
|
|
280
|
+
when :ptr; " d#{%w[x b w x d x x x q][@parser.typesize[type.name]]} "
|
|
281
|
+
when :float; ' db ' + [value].pack(@parser.endianness == :little ? 'e' : 'g').unpack('C*').join(', ') + ' // '
|
|
282
|
+
when :double; ' db ' + [value].pack(@parser.endianness == :little ? 'E' : 'G').unpack('C*').join(', ') + ' // '
|
|
283
|
+
when :longdouble; ' db ' + [value].pack(@parser.endianness == :little ? 'E' : 'G').unpack('C*').join(', ') + ' // ' # XXX same as :double
|
|
284
|
+
else raise "unknown idata type #{type.inspect} #{value.inspect}"
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
@source.last << c_idata_inner_cexpr(value)
|
|
288
|
+
|
|
289
|
+
@parser.typesize[type.name]
|
|
290
|
+
|
|
291
|
+
when Struct
|
|
292
|
+
value ||= []
|
|
293
|
+
@source.last << ':' if not @source.last.empty?
|
|
294
|
+
# could .align here, but if there is our label name just before, it should have been .aligned too..
|
|
295
|
+
raise "unknown struct initializer #{value.inspect}" if not value.kind_of? ::Array
|
|
296
|
+
sz = 0
|
|
297
|
+
type.members.zip(value).each { |m, v|
|
|
298
|
+
if m.name and wsz = type.offsetof(@parser, m.name) and sz < wsz
|
|
299
|
+
@source << "db #{wsz-sz} dup(?)"
|
|
300
|
+
end
|
|
301
|
+
@source << ''
|
|
302
|
+
flen = c_idata_inner(m.type, v)
|
|
303
|
+
sz += flen
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
sz
|
|
307
|
+
|
|
308
|
+
when Union
|
|
309
|
+
value ||= []
|
|
310
|
+
@source.last << ':' if not @source.last.empty?
|
|
311
|
+
len = sizeof(nil, type)
|
|
312
|
+
raise "unknown union initializer #{value.inspect}" if not value.kind_of? ::Array
|
|
313
|
+
idx = value.rindex(value.compact.last) || 0
|
|
314
|
+
raise "empty union initializer" if not idx
|
|
315
|
+
wlen = c_idata_inner(type.members[idx].type, value[idx])
|
|
316
|
+
@source << "db #{'0' * (len - wlen) * ', '}" if wlen < len
|
|
317
|
+
|
|
318
|
+
len
|
|
319
|
+
|
|
320
|
+
when Array
|
|
321
|
+
value ||= []
|
|
322
|
+
if value.kind_of? CExpression and not value.op and value.rexpr.kind_of? ::String
|
|
323
|
+
elen = sizeof(nil, value.type.type)
|
|
324
|
+
@source.last <<
|
|
325
|
+
case elen
|
|
326
|
+
when 1; ' db '
|
|
327
|
+
when 2; ' dw '
|
|
328
|
+
else raise 'bad char* type ' + value.inspect
|
|
329
|
+
end << value.rexpr.inspect
|
|
330
|
+
|
|
331
|
+
len = type.length || (value.rexpr.length+1)
|
|
332
|
+
if len > value.rexpr.length
|
|
333
|
+
@source.last << (', 0' * (len - value.rexpr.length))
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
elen * len
|
|
337
|
+
|
|
338
|
+
elsif value.kind_of? ::Array
|
|
339
|
+
@source.last << ':' if not @source.last.empty?
|
|
340
|
+
len = type.length || value.length
|
|
341
|
+
value.each { |v|
|
|
342
|
+
@source << ''
|
|
343
|
+
c_idata_inner(type.type, v)
|
|
344
|
+
}
|
|
345
|
+
len -= value.length
|
|
346
|
+
if len > 0
|
|
347
|
+
@source << " db #{len * sizeof(nil, type.type)} dup(0)"
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
sizeof(nil, type.type) * len
|
|
351
|
+
|
|
352
|
+
else raise "unknown static array initializer #{value.inspect}"
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def c_idata_inner_cexpr(expr)
|
|
358
|
+
expr = expr.reduce(@parser) if expr.kind_of? CExpression
|
|
359
|
+
case expr
|
|
360
|
+
when ::Integer; (expr >= 4096) ? ('0x%X' % expr) : expr.to_s
|
|
361
|
+
when ::Numeric; expr.to_s
|
|
362
|
+
when Variable
|
|
363
|
+
case expr.type
|
|
364
|
+
when Array; expr.name
|
|
365
|
+
else c_idata_inner_cexpr(expr.initializer)
|
|
366
|
+
end
|
|
367
|
+
when CExpression
|
|
368
|
+
if not expr.lexpr
|
|
369
|
+
case expr.op
|
|
370
|
+
when :&
|
|
371
|
+
case expr.rexpr
|
|
372
|
+
when Variable; expr.rexpr.name
|
|
373
|
+
else raise 'unhandled addrof in initializer ' + expr.rexpr.inspect
|
|
374
|
+
end
|
|
375
|
+
#when :*
|
|
376
|
+
when :+; c_idata_inner_cexpr(expr.rexpr)
|
|
377
|
+
when :-; ' -' << c_idata_inner_cexpr(expr.rexpr)
|
|
378
|
+
when nil
|
|
379
|
+
e = c_idata_inner_cexpr(expr.rexpr)
|
|
380
|
+
if expr.rexpr.kind_of? CExpression
|
|
381
|
+
e = '(' << e << " & 0#{'ff'*sizeof(expr)}h)"
|
|
382
|
+
end
|
|
383
|
+
e
|
|
384
|
+
else raise 'unhandled initializer expr ' + expr.inspect
|
|
385
|
+
end
|
|
386
|
+
else
|
|
387
|
+
case expr.op
|
|
388
|
+
when :+, :-, :*, :/, :%, :<<, :>>, :&, :|, :^
|
|
389
|
+
e = '(' << c_idata_inner_cexpr(expr.lexpr) <<
|
|
390
|
+
expr.op.to_s << c_idata_inner_cexpr(expr.rexpr) << ')'
|
|
391
|
+
if expr.type.integral?
|
|
392
|
+
# db are unsigned
|
|
393
|
+
e = '(' << e << " & 0#{'ff'*sizeof(expr)}h)"
|
|
394
|
+
end
|
|
395
|
+
e
|
|
396
|
+
#when :'.'
|
|
397
|
+
#when :'->'
|
|
398
|
+
#when :'[]'
|
|
399
|
+
else raise 'unhandled initializer expr ' + expr.inspect
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
else raise 'unhandled initializer ' + expr.inspect
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def c_udata(data, align)
|
|
407
|
+
@source << "#{@label_oldname[data.name]}:" if @label_oldname[data.name]
|
|
408
|
+
@source << "#{data.name} "
|
|
409
|
+
@source.last <<
|
|
410
|
+
case data.type
|
|
411
|
+
when BaseType
|
|
412
|
+
len = @parser.typesize[data.type.name]
|
|
413
|
+
case data.type.name
|
|
414
|
+
when :__int8; 'db ?'
|
|
415
|
+
when :__int16; 'dw ?'
|
|
416
|
+
when :__int32; 'dd ?'
|
|
417
|
+
when :__int64; 'dq ?'
|
|
418
|
+
else "db #{len} dup(?)"
|
|
419
|
+
end
|
|
420
|
+
else
|
|
421
|
+
len = sizeof(data)
|
|
422
|
+
"db #{len} dup(?)"
|
|
423
|
+
end
|
|
424
|
+
len %= align
|
|
425
|
+
len == 0 ? align : len
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# return non-nil if the variable name is unsuitable to appear as is in the asm listing
|
|
429
|
+
# eg filter out asm instruction names
|
|
430
|
+
def check_reserved_name(var)
|
|
431
|
+
%w[db dw dd dq].include?(var.name)
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
class Statement
|
|
436
|
+
# all Statements/Declaration must define a precompile(compiler, scope) method
|
|
437
|
+
# it must append itself to scope.statements
|
|
438
|
+
|
|
439
|
+
# turns a statement into a new block
|
|
440
|
+
def precompile_make_block(scope)
|
|
441
|
+
b = Block.new scope
|
|
442
|
+
b.statements << self
|
|
443
|
+
b
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
class Block
|
|
448
|
+
# precompile all statements, then simplifies symbols/structs types
|
|
449
|
+
def precompile(compiler, scope=nil)
|
|
450
|
+
stmts = @statements.dup
|
|
451
|
+
@statements.clear
|
|
452
|
+
stmts.each { |st|
|
|
453
|
+
compiler.curexpr = st
|
|
454
|
+
st.precompile(compiler, self)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
# cleanup declarations
|
|
458
|
+
@symbol.delete_if { |n, s| not s.kind_of? Variable }
|
|
459
|
+
@struct.delete_if { |n, s| not s.kind_of? Union }
|
|
460
|
+
@symbol.each_value { |var|
|
|
461
|
+
CExpression.precompile_type(compiler, self, var, true)
|
|
462
|
+
}
|
|
463
|
+
@struct.each_value { |var|
|
|
464
|
+
next if not var.members
|
|
465
|
+
var.members.each { |m|
|
|
466
|
+
CExpression.precompile_type(compiler, self, m, true)
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
scope.statements << self if scope and not @statements.empty?
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# removes unused labels, and in-place goto (goto toto; toto:)
|
|
473
|
+
def precompile_optimize
|
|
474
|
+
list = []
|
|
475
|
+
precompile_optimize_inner(list, 1)
|
|
476
|
+
precompile_optimize_inner(list, 2)
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# step 1: list used labels/unused goto
|
|
480
|
+
# step 2: remove unused labels
|
|
481
|
+
def precompile_optimize_inner(list, step)
|
|
482
|
+
lastgoto = nil
|
|
483
|
+
hadref = false
|
|
484
|
+
walk = lambda { |expr|
|
|
485
|
+
next if not expr.kind_of? CExpression
|
|
486
|
+
# gcc's unary && support
|
|
487
|
+
if not expr.op and not expr.lexpr and expr.rexpr.kind_of? Label
|
|
488
|
+
list << expr.rexpr.name
|
|
489
|
+
else
|
|
490
|
+
walk[expr.lexpr]
|
|
491
|
+
if expr.rexpr.kind_of? ::Array
|
|
492
|
+
expr.rexpr.each { |r| walk[r] }
|
|
493
|
+
else
|
|
494
|
+
walk[expr.rexpr]
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
}
|
|
498
|
+
@statements.dup.each { |s|
|
|
499
|
+
lastgoto = nil if not s.kind_of? Label
|
|
500
|
+
case s
|
|
501
|
+
when Block
|
|
502
|
+
s.precompile_optimize_inner(list, step)
|
|
503
|
+
@statements.delete s if step == 2 and s.statements.empty?
|
|
504
|
+
when CExpression; walk[s] if step == 1
|
|
505
|
+
when Label
|
|
506
|
+
case step
|
|
507
|
+
when 1
|
|
508
|
+
if lastgoto and lastgoto.target == s.name
|
|
509
|
+
list << lastgoto
|
|
510
|
+
list.delete s.name if not hadref
|
|
511
|
+
end
|
|
512
|
+
when 2; @statements.delete s if not list.include? s.name
|
|
513
|
+
end
|
|
514
|
+
when Goto, If
|
|
515
|
+
s.kind_of?(If) ? g = s.bthen : g = s
|
|
516
|
+
case step
|
|
517
|
+
when 1
|
|
518
|
+
hadref = list.include? g.target
|
|
519
|
+
lastgoto = g
|
|
520
|
+
list << g.target
|
|
521
|
+
when 2
|
|
522
|
+
if list.include? g
|
|
523
|
+
idx = @statements.index s
|
|
524
|
+
@statements.delete s
|
|
525
|
+
@statements[idx, 0] = s.test if s != g and not s.test.constant?
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
}
|
|
530
|
+
list
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
# noop
|
|
534
|
+
def precompile_make_block(scope) self end
|
|
535
|
+
|
|
536
|
+
def continue_label ; defined?(@continue_label) ? @continue_label : @outer.continue_label end
|
|
537
|
+
def continue_label=(l) @continue_label = l end
|
|
538
|
+
def break_label ; defined?(@break_label) ? @break_label : @outer.break_label end
|
|
539
|
+
def break_label=(l) @break_label = l end
|
|
540
|
+
def return_label ; defined?(@return_label) ? @return_label : @outer.return_label end
|
|
541
|
+
def return_label=(l) @return_label = l end
|
|
542
|
+
def nonauto_label=(l) @nonauto_label = l end
|
|
543
|
+
def nonauto_label ; defined?(@nonauto_label) ? @nonauto_label : @outer.nonauto_label end
|
|
544
|
+
def function ; defined?(@function) ? @function : @outer.function end
|
|
545
|
+
def function=(f) @function = f end
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
class Declaration
|
|
549
|
+
def precompile(compiler, scope)
|
|
550
|
+
if (@var.type.kind_of? Function and @var.initializer and scope != compiler.toplevel) or @var.storage == :static or compiler.check_reserved_name(@var)
|
|
551
|
+
old = @var.name
|
|
552
|
+
ref = scope.symbol.delete old
|
|
553
|
+
if scope == compiler.toplevel or (@var.type.kind_of?(Function) and not @var.initializer)
|
|
554
|
+
if n = compiler.label_oldname.index(old)
|
|
555
|
+
# reuse same name as predeclarations
|
|
556
|
+
@var.name = n
|
|
557
|
+
else
|
|
558
|
+
@var.name = compiler.new_label @var.name until @var.name != old
|
|
559
|
+
compiler.label_oldname[@var.name] = old
|
|
560
|
+
end
|
|
561
|
+
ref ||= scope.symbol[@var.name] || @var
|
|
562
|
+
# append only one actual declaration for all predecls (the one with init, or the last uninit)
|
|
563
|
+
scope.statements << self if ref.eql?(@var)
|
|
564
|
+
else
|
|
565
|
+
@var.name = compiler.new_label @var.name until @var.name != old
|
|
566
|
+
compiler.toplevel.statements << self
|
|
567
|
+
end
|
|
568
|
+
compiler.toplevel.symbol[@var.name] = ref
|
|
569
|
+
else
|
|
570
|
+
scope.symbol[@var.name] ||= @var
|
|
571
|
+
appendme = true if scope.symbol[@var.name].eql?(@var)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
if i = @var.initializer
|
|
575
|
+
if @var.type.kind_of? Function
|
|
576
|
+
if @var.type.type.kind_of? Union
|
|
577
|
+
s = @var.type.type
|
|
578
|
+
v = Variable.new
|
|
579
|
+
v.name = compiler.new_label('return_struct_ptr')
|
|
580
|
+
v.type = Pointer.new(s)
|
|
581
|
+
CExpression.precompile_type(compiler, scope, v)
|
|
582
|
+
@var.type.args.unshift v
|
|
583
|
+
@var.type.type = v.type
|
|
584
|
+
end
|
|
585
|
+
i.function = @var
|
|
586
|
+
i.return_label = compiler.new_label('epilog')
|
|
587
|
+
i.nonauto_label = {}
|
|
588
|
+
i.precompile(compiler)
|
|
589
|
+
Label.new(i.return_label).precompile(compiler, i)
|
|
590
|
+
i.precompile_optimize
|
|
591
|
+
# append now so that static dependencies are declared before us
|
|
592
|
+
# TODO no pure inline if addrof(func) needed
|
|
593
|
+
scope.statements << self if appendme and not @var.attributes.to_a.include? 'inline'
|
|
594
|
+
elsif scope != compiler.toplevel and @var.storage != :static
|
|
595
|
+
scope.statements << self if appendme
|
|
596
|
+
Declaration.precompile_dyn_initializer(compiler, scope, @var, @var.type, i)
|
|
597
|
+
@var.initializer = nil
|
|
598
|
+
else
|
|
599
|
+
scope.statements << self if appendme
|
|
600
|
+
@var.initializer = Declaration.precompile_static_initializer(compiler, @var.type, i)
|
|
601
|
+
end
|
|
602
|
+
else
|
|
603
|
+
scope.statements << self if appendme
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
# turns an initializer to CExpressions in scope.statements
|
|
608
|
+
def self.precompile_dyn_initializer(compiler, scope, var, type, init)
|
|
609
|
+
case type = type.untypedef
|
|
610
|
+
when Array
|
|
611
|
+
# XXX TODO type.length may be dynamic !!
|
|
612
|
+
case init
|
|
613
|
+
when CExpression
|
|
614
|
+
# char toto[] = "42"
|
|
615
|
+
if not init.kind_of? CExpression or init.op or init.lexpr or not init.rexpr.kind_of? ::String
|
|
616
|
+
raise "unknown initializer #{init.inspect} for #{var.inspect}"
|
|
617
|
+
end
|
|
618
|
+
init = init.rexpr.unpack('C*') + [0]
|
|
619
|
+
init.map! { |chr| CExpression.new(nil, nil, chr, type.type) }
|
|
620
|
+
precompile_dyn_initializer(compiler, scope, var, type, init)
|
|
621
|
+
|
|
622
|
+
when ::Array
|
|
623
|
+
type.length ||= init.length
|
|
624
|
+
# len is an Integer
|
|
625
|
+
init.each_with_index { |it, idx|
|
|
626
|
+
next if not it
|
|
627
|
+
break if idx >= type.length
|
|
628
|
+
idx = CExpression.new(nil, nil, idx, BaseType.new(:long, :unsigned))
|
|
629
|
+
v = CExpression.new(var, :'[]', idx, type.type)
|
|
630
|
+
precompile_dyn_initializer(compiler, scope, v, type.type, it)
|
|
631
|
+
}
|
|
632
|
+
else raise "unknown initializer #{init.inspect} for #{var.inspect}"
|
|
633
|
+
end
|
|
634
|
+
when Union
|
|
635
|
+
case init
|
|
636
|
+
when CExpression, Variable
|
|
637
|
+
if init.type.untypedef.kind_of? BaseType
|
|
638
|
+
# works for struct foo bar[] = {0}; ...
|
|
639
|
+
type.members.each { |m|
|
|
640
|
+
v = CExpression.new(var, :'.', m.name, m.type)
|
|
641
|
+
precompile_dyn_initializer(compiler, scope, v, v.type, init)
|
|
642
|
+
}
|
|
643
|
+
elsif init.type.untypedef.kind_of? type.class
|
|
644
|
+
CExpression.new(var, :'=', init, type).precompile(compiler, scope)
|
|
645
|
+
else
|
|
646
|
+
raise "bad initializer #{init.inspect} for #{var.inspect}"
|
|
647
|
+
end
|
|
648
|
+
when ::Array
|
|
649
|
+
init.each_with_index{ |it, idx|
|
|
650
|
+
next if not it
|
|
651
|
+
m = type.members[idx]
|
|
652
|
+
v = CExpression.new(var, :'.', m.name, m.type)
|
|
653
|
+
precompile_dyn_initializer(compiler, scope, v, m.type, it)
|
|
654
|
+
}
|
|
655
|
+
else raise "unknown initializer #{init.inspect} for #{var.inspect}"
|
|
656
|
+
end
|
|
657
|
+
else
|
|
658
|
+
case init
|
|
659
|
+
when CExpression
|
|
660
|
+
CExpression.new(var, :'=', init, type).precompile(compiler, scope)
|
|
661
|
+
else raise "unknown initializer #{init.inspect} for #{var.inspect}"
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
# returns a precompiled static initializer (eg string constants)
|
|
667
|
+
def self.precompile_static_initializer(compiler, type, init)
|
|
668
|
+
# TODO
|
|
669
|
+
case type = type.untypedef
|
|
670
|
+
when Array
|
|
671
|
+
if init.kind_of? ::Array
|
|
672
|
+
init.map { |i| precompile_static_initializer(compiler, type.type, i) }
|
|
673
|
+
else
|
|
674
|
+
init
|
|
675
|
+
end
|
|
676
|
+
when Union
|
|
677
|
+
if init.kind_of? ::Array
|
|
678
|
+
init.zip(type.members).map { |i, m| precompile_static_initializer(compiler, m.type, i) }
|
|
679
|
+
else
|
|
680
|
+
init
|
|
681
|
+
end
|
|
682
|
+
else
|
|
683
|
+
if init.kind_of? CExpression and init = init.reduce(compiler) and init.kind_of? CExpression
|
|
684
|
+
if not init.op and init.rexpr.kind_of? ::String
|
|
685
|
+
v = Variable.new
|
|
686
|
+
v.storage = :static
|
|
687
|
+
v.name = 'char_' + init.rexpr.tr('^a-zA-Z', '')[0, 8]
|
|
688
|
+
v.type = Array.new(type.type)
|
|
689
|
+
v.type.length = init.rexpr.length + 1
|
|
690
|
+
v.type.type.qualifier = [:const]
|
|
691
|
+
v.initializer = CExpression.new(nil, nil, init.rexpr, type)
|
|
692
|
+
Declaration.new(v).precompile(compiler, compiler.toplevel)
|
|
693
|
+
init.rexpr = v
|
|
694
|
+
end
|
|
695
|
+
init.rexpr = precompile_static_initializer(compiler, init.rexpr.type, init.rexpr) if init.rexpr.kind_of? CExpression
|
|
696
|
+
init.lexpr = precompile_static_initializer(compiler, init.lexpr.type, init.lexpr) if init.lexpr.kind_of? CExpression
|
|
697
|
+
end
|
|
698
|
+
init
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
class If
|
|
704
|
+
def precompile(compiler, scope)
|
|
705
|
+
expr = lambda { |e| e.kind_of?(CExpression) ? e : CExpression.new(nil, nil, e, e.type) }
|
|
706
|
+
|
|
707
|
+
if @bthen.kind_of? Goto or @bthen.kind_of? Break or @bthen.kind_of? Continue
|
|
708
|
+
# if () goto l; else b; => if () goto l; b;
|
|
709
|
+
if belse
|
|
710
|
+
t1 = @belse
|
|
711
|
+
@belse = nil
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# need to convert user-defined Goto target !
|
|
715
|
+
@bthen.precompile(compiler, scope)
|
|
716
|
+
@bthen = scope.statements.pop # break => goto break_label
|
|
717
|
+
elsif belse
|
|
718
|
+
# if () a; else b; => if () goto then; b; goto end; then: a; end:
|
|
719
|
+
t1 = @belse
|
|
720
|
+
t2 = @bthen
|
|
721
|
+
l2 = compiler.new_label('if_then')
|
|
722
|
+
@bthen = Goto.new(l2)
|
|
723
|
+
@belse = nil
|
|
724
|
+
l3 = compiler.new_label('if_end')
|
|
725
|
+
else
|
|
726
|
+
# if () a; => if (!) goto end; a; end:
|
|
727
|
+
t1 = @bthen
|
|
728
|
+
l2 = compiler.new_label('if_end')
|
|
729
|
+
@bthen = Goto.new(l2)
|
|
730
|
+
@test = CExpression.negate(@test)
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
@test = expr[@test]
|
|
734
|
+
case @test.op
|
|
735
|
+
when :'&&'
|
|
736
|
+
# if (c1 && c2) goto a; => if (!c1) goto b; if (c2) goto a; b:
|
|
737
|
+
l1 = compiler.new_label('if_nand')
|
|
738
|
+
If.new(CExpression.negate(@test.lexpr), Goto.new(l1)).precompile(compiler, scope)
|
|
739
|
+
@test = expr[@test.rexpr]
|
|
740
|
+
precompile(compiler, scope)
|
|
741
|
+
when :'||'
|
|
742
|
+
l1 = compiler.new_label('if_or')
|
|
743
|
+
If.new(expr[@test.lexpr], Goto.new(@bthen.target)).precompile(compiler, scope)
|
|
744
|
+
@test = expr[@test.rexpr]
|
|
745
|
+
precompile(compiler, scope)
|
|
746
|
+
else
|
|
747
|
+
@test = CExpression.precompile_inner(compiler, scope, @test)
|
|
748
|
+
t = @test.reduce(compiler)
|
|
749
|
+
if t.kind_of? ::Integer
|
|
750
|
+
if t == 0
|
|
751
|
+
Label.new(l1, nil).precompile(compiler, scope) if l1
|
|
752
|
+
t1.precompile(compiler, scope) if t1
|
|
753
|
+
Label.new(l2, nil).precompile(compiler, scope) if l2
|
|
754
|
+
Label.new(l3, nil).precompile(compiler, scope) if l3
|
|
755
|
+
else
|
|
756
|
+
scope.statements << @bthen
|
|
757
|
+
Label.new(l1, nil).precompile(compiler, scope) if l1
|
|
758
|
+
Label.new(l2, nil).precompile(compiler, scope) if l2
|
|
759
|
+
t2.precompile(compiler, scope) if t2
|
|
760
|
+
Label.new(l3, nil).precompile(compiler, scope) if l3
|
|
761
|
+
end
|
|
762
|
+
return
|
|
763
|
+
end
|
|
764
|
+
scope.statements << self
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
Label.new(l1, nil).precompile(compiler, scope) if l1
|
|
768
|
+
t1.precompile(compiler, scope) if t1
|
|
769
|
+
Goto.new(l3).precompile(compiler, scope) if l3
|
|
770
|
+
Label.new(l2, nil).precompile(compiler, scope) if l2
|
|
771
|
+
t2.precompile(compiler, scope) if t2
|
|
772
|
+
Label.new(l3, nil).precompile(compiler, scope) if l3
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
class For
|
|
777
|
+
def precompile(compiler, scope)
|
|
778
|
+
if init
|
|
779
|
+
@init.precompile(compiler, scope)
|
|
780
|
+
scope = @init if @init.kind_of? Block
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
@body = @body.precompile_make_block scope
|
|
784
|
+
@body.continue_label = compiler.new_label 'for_continue'
|
|
785
|
+
@body.break_label = compiler.new_label 'for_break'
|
|
786
|
+
label_test = compiler.new_label 'for_test'
|
|
787
|
+
|
|
788
|
+
Label.new(label_test).precompile(compiler, scope)
|
|
789
|
+
if test
|
|
790
|
+
If.new(CExpression.negate(@test), Goto.new(@body.break_label)).precompile(compiler, scope)
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
@body.precompile(compiler, scope)
|
|
794
|
+
|
|
795
|
+
Label.new(@body.continue_label).precompile(compiler, scope)
|
|
796
|
+
if iter
|
|
797
|
+
@iter.precompile(compiler, scope)
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
Goto.new(label_test).precompile(compiler, scope)
|
|
801
|
+
Label.new(@body.break_label).precompile(compiler, scope)
|
|
802
|
+
end
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
class While
|
|
806
|
+
def precompile(compiler, scope)
|
|
807
|
+
@body = @body.precompile_make_block scope
|
|
808
|
+
@body.continue_label = compiler.new_label('while_continue')
|
|
809
|
+
@body.break_label = compiler.new_label('while_break')
|
|
810
|
+
|
|
811
|
+
Label.new(@body.continue_label).precompile(compiler, scope)
|
|
812
|
+
|
|
813
|
+
If.new(CExpression.negate(@test), Goto.new(@body.break_label)).precompile(compiler, scope)
|
|
814
|
+
|
|
815
|
+
@body.precompile(compiler, scope)
|
|
816
|
+
|
|
817
|
+
Goto.new(@body.continue_label).precompile(compiler, scope)
|
|
818
|
+
Label.new(@body.break_label).precompile(compiler, scope)
|
|
819
|
+
end
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
class DoWhile
|
|
823
|
+
def precompile(compiler, scope)
|
|
824
|
+
@body = @body.precompile_make_block scope
|
|
825
|
+
@body.continue_label = compiler.new_label('dowhile_continue')
|
|
826
|
+
@body.break_label = compiler.new_label('dowhile_break')
|
|
827
|
+
loop_start = compiler.new_label('dowhile_start')
|
|
828
|
+
|
|
829
|
+
Label.new(loop_start).precompile(compiler, scope)
|
|
830
|
+
|
|
831
|
+
@body.precompile(compiler, scope)
|
|
832
|
+
|
|
833
|
+
Label.new(@body.continue_label).precompile(compiler, scope)
|
|
834
|
+
|
|
835
|
+
If.new(@test, Goto.new(loop_start)).precompile(compiler, scope)
|
|
836
|
+
|
|
837
|
+
Label.new(@body.break_label).precompile(compiler, scope)
|
|
838
|
+
end
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
class Switch
|
|
842
|
+
def precompile(compiler, scope)
|
|
843
|
+
var = Variable.new
|
|
844
|
+
var.storage = :register
|
|
845
|
+
var.name = compiler.new_label('switch')
|
|
846
|
+
var.type = @test.type
|
|
847
|
+
var.initializer = @test
|
|
848
|
+
CExpression.precompile_type(compiler, scope, var)
|
|
849
|
+
Declaration.new(var).precompile(compiler, scope)
|
|
850
|
+
|
|
851
|
+
@body = @body.precompile_make_block scope
|
|
852
|
+
@body.break_label = compiler.new_label('switch_break')
|
|
853
|
+
@body.precompile(compiler)
|
|
854
|
+
default = @body.break_label
|
|
855
|
+
# recursive lambda to change Case to Labels
|
|
856
|
+
# dynamically creates the If sequence
|
|
857
|
+
walk = lambda { |blk|
|
|
858
|
+
blk.statements.each_with_index { |s, i|
|
|
859
|
+
case s
|
|
860
|
+
when Case
|
|
861
|
+
label = compiler.new_label('case')
|
|
862
|
+
if s.expr == 'default'
|
|
863
|
+
default = label
|
|
864
|
+
elsif s.exprup
|
|
865
|
+
If.new(CExpression.new(CExpression.new(var, :'>=', s.expr, BaseType.new(:int)), :'&&',
|
|
866
|
+
CExpression.new(var, :'<=', s.exprup, BaseType.new(:int)),
|
|
867
|
+
BaseType.new(:int)), Goto.new(label)).precompile(compiler, scope)
|
|
868
|
+
else
|
|
869
|
+
If.new(CExpression.new(var, :'==', s.expr, BaseType.new(:int)),
|
|
870
|
+
Goto.new(label)).precompile(compiler, scope)
|
|
871
|
+
end
|
|
872
|
+
blk.statements[i] = Label.new(label)
|
|
873
|
+
when Block
|
|
874
|
+
walk[s]
|
|
875
|
+
end
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
walk[@body]
|
|
879
|
+
Goto.new(default).precompile(compiler, scope)
|
|
880
|
+
scope.statements << @body
|
|
881
|
+
Label.new(@body.break_label).precompile(compiler, scope)
|
|
882
|
+
end
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
class Continue
|
|
886
|
+
def precompile(compiler, scope)
|
|
887
|
+
Goto.new(scope.continue_label).precompile(compiler, scope)
|
|
888
|
+
end
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
class Break
|
|
892
|
+
def precompile(compiler, scope)
|
|
893
|
+
Goto.new(scope.break_label).precompile(compiler, scope)
|
|
894
|
+
end
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
class Return
|
|
898
|
+
def precompile(compiler, scope)
|
|
899
|
+
if @value
|
|
900
|
+
@value = CExpression.new(nil, nil, @value, @value.type) if not @value.kind_of? CExpression
|
|
901
|
+
if @value.type.untypedef.kind_of? Union
|
|
902
|
+
@value = @value.precompile_inner(compiler, scope)
|
|
903
|
+
func = scope.function.type
|
|
904
|
+
CExpression.new(CExpression.new(nil, :*, func.args.first, @value.type), :'=', @value, @value.type).precompile(compiler, scope)
|
|
905
|
+
@value = func.args.first
|
|
906
|
+
else
|
|
907
|
+
# cast to function return type
|
|
908
|
+
@value = CExpression.new(nil, nil, @value, scope.function.type.type).precompile_inner(compiler, scope)
|
|
909
|
+
end
|
|
910
|
+
scope.statements << self
|
|
911
|
+
end
|
|
912
|
+
Goto.new(scope.return_label).precompile(compiler, scope)
|
|
913
|
+
end
|
|
914
|
+
end
|
|
915
|
+
|
|
916
|
+
class Label
|
|
917
|
+
def precompile(compiler, scope)
|
|
918
|
+
if name and (not compiler.auto_label_list[@name])
|
|
919
|
+
@name = scope.nonauto_label[@name] ||= compiler.new_label(@name)
|
|
920
|
+
end
|
|
921
|
+
scope.statements << self
|
|
922
|
+
if statement
|
|
923
|
+
@statement.precompile(compiler, scope)
|
|
924
|
+
@statement = nil
|
|
925
|
+
end
|
|
926
|
+
end
|
|
927
|
+
end
|
|
928
|
+
|
|
929
|
+
class Case
|
|
930
|
+
def precompile(compiler, scope)
|
|
931
|
+
@expr = CExpression.precompile_inner(compiler, scope, @expr)
|
|
932
|
+
@exprup = CExpression.precompile_inner(compiler, scope, @exprup) if exprup
|
|
933
|
+
super(compiler, scope)
|
|
934
|
+
end
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
class Goto
|
|
938
|
+
def precompile(compiler, scope)
|
|
939
|
+
if not compiler.auto_label_list[@target]
|
|
940
|
+
@target = scope.nonauto_label[@target] ||= compiler.new_label(@target)
|
|
941
|
+
end
|
|
942
|
+
scope.statements << self
|
|
943
|
+
end
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
class Asm
|
|
947
|
+
def precompile(compiler, scope)
|
|
948
|
+
scope.statements << self
|
|
949
|
+
# TODO CExpr.precompile_type(clobbers)
|
|
950
|
+
end
|
|
951
|
+
end
|
|
952
|
+
|
|
953
|
+
class CExpression
|
|
954
|
+
def precompile(compiler, scope)
|
|
955
|
+
i = precompile_inner(compiler, scope, false)
|
|
956
|
+
scope.statements << i if i
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
# changes obj.type to a precompiled type
|
|
960
|
+
# keeps struct/union, change everything else to __int\d
|
|
961
|
+
# except Arrays if declaration is true (need to know variable allocation sizes etc)
|
|
962
|
+
# returns the type
|
|
963
|
+
def self.precompile_type(compiler, scope, obj, declaration = false)
|
|
964
|
+
case t = obj.type.untypedef
|
|
965
|
+
when BaseType
|
|
966
|
+
case t.name
|
|
967
|
+
when :void
|
|
968
|
+
when :float, :double, :longdouble
|
|
969
|
+
else t = BaseType.new("__int#{compiler.typesize[t.name]*8}".to_sym, t.specifier)
|
|
970
|
+
end
|
|
971
|
+
when Array
|
|
972
|
+
if declaration; precompile_type(compiler, scope, t, declaration)
|
|
973
|
+
else t = BaseType.new("__int#{compiler.typesize[:ptr]*8}".to_sym, :unsigned)
|
|
974
|
+
end
|
|
975
|
+
when Pointer
|
|
976
|
+
if t.type.untypedef.kind_of? Function
|
|
977
|
+
precompile_type(compiler, scope, t, declaration)
|
|
978
|
+
else
|
|
979
|
+
t = BaseType.new("__int#{compiler.typesize[:ptr]*8}".to_sym, :unsigned)
|
|
980
|
+
end
|
|
981
|
+
when Enum; t = BaseType.new("__int#{compiler.typesize[:int]*8}".to_sym)
|
|
982
|
+
when Function
|
|
983
|
+
precompile_type(compiler, scope, t)
|
|
984
|
+
t.args ||= []
|
|
985
|
+
t.args.each { |a| precompile_type(compiler, scope, a) }
|
|
986
|
+
when Union
|
|
987
|
+
if declaration and t.members and not t.name # anonymous struct
|
|
988
|
+
t.members.each { |a| precompile_type(compiler, scope, a, true) }
|
|
989
|
+
end
|
|
990
|
+
else raise 'bad type ' + t.inspect
|
|
991
|
+
end
|
|
992
|
+
(t.qualifier ||= []).concat obj.type.qualifier if obj.type.qualifier and t != obj.type
|
|
993
|
+
(t.attributes ||= []).concat obj.type.attributes if obj.type.attributes and t != obj.type
|
|
994
|
+
while obj.type.kind_of? TypeDef
|
|
995
|
+
obj.type = obj.type.type
|
|
996
|
+
(t.qualifier ||= []).concat obj.type.qualifier if obj.type.qualifier and t != obj.type
|
|
997
|
+
(t.attributes ||= []).concat obj.type.attributes if obj.type.attributes and t != obj.type
|
|
998
|
+
end
|
|
999
|
+
obj.type = t
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
def self.precompile_inner(compiler, scope, expr, nested = true)
|
|
1003
|
+
case expr
|
|
1004
|
+
when CExpression; expr.precompile_inner(compiler, scope, nested)
|
|
1005
|
+
else expr
|
|
1006
|
+
end
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
# returns a new CExpression with simplified self.type, computes structure offsets
|
|
1010
|
+
# turns char[]/float immediates to reference to anonymised const
|
|
1011
|
+
# TODO 'a = b += c' => 'b += c; a = b' (use nested argument)
|
|
1012
|
+
# TODO handle precompile_inner return nil
|
|
1013
|
+
# TODO struct.bits
|
|
1014
|
+
def precompile_inner(compiler, scope, nested = true)
|
|
1015
|
+
case @op
|
|
1016
|
+
when :'.'
|
|
1017
|
+
# a.b => (&a)->b
|
|
1018
|
+
lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
|
|
1019
|
+
ll = lexpr
|
|
1020
|
+
ll = lexpr.rexpr while ll.kind_of? CExpression and not ll.op
|
|
1021
|
+
if ll.kind_of? CExpression and ll.op == :'*' and not ll.lexpr
|
|
1022
|
+
# do not change lexpr.rexpr.type directly to a pointer, might retrigger (ptr+imm) => (ptr + imm*sizeof(*ptr))
|
|
1023
|
+
@lexpr = CExpression.new(nil, nil, ll.rexpr, Pointer.new(lexpr.type))
|
|
1024
|
+
else
|
|
1025
|
+
@lexpr = CExpression.new(nil, :'&', lexpr, Pointer.new(lexpr.type))
|
|
1026
|
+
end
|
|
1027
|
+
@op = :'->'
|
|
1028
|
+
precompile_inner(compiler, scope)
|
|
1029
|
+
when :'->'
|
|
1030
|
+
# a->b => *(a + off(b))
|
|
1031
|
+
struct = @lexpr.type.untypedef.type.untypedef
|
|
1032
|
+
lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
|
|
1033
|
+
@lexpr = nil
|
|
1034
|
+
@op = nil
|
|
1035
|
+
if struct.kind_of? Union and (off = struct.offsetof(compiler, @rexpr)) != 0
|
|
1036
|
+
off = CExpression.new(nil, nil, off, BaseType.new(:int, :unsigned))
|
|
1037
|
+
@rexpr = CExpression.new(lexpr, :'+', off, lexpr.type)
|
|
1038
|
+
# ensure the (ptr + value) is not expanded to (ptr + value * sizeof(*ptr))
|
|
1039
|
+
CExpression.precompile_type(compiler, scope, @rexpr)
|
|
1040
|
+
else
|
|
1041
|
+
# union or 1st struct member
|
|
1042
|
+
@rexpr = lexpr
|
|
1043
|
+
end
|
|
1044
|
+
if @type.kind_of? Array # Array member type is already an address
|
|
1045
|
+
else
|
|
1046
|
+
@rexpr = CExpression.new(nil, :*, @rexpr, @rexpr.type)
|
|
1047
|
+
end
|
|
1048
|
+
precompile_inner(compiler, scope)
|
|
1049
|
+
when :'[]'
|
|
1050
|
+
rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1051
|
+
if rexpr.kind_of? CExpression and not rexpr.op and rexpr.rexpr == 0
|
|
1052
|
+
@rexpr = @lexpr
|
|
1053
|
+
else
|
|
1054
|
+
@rexpr = CExpression.new(@lexpr, :'+', rexpr, @lexpr.type)
|
|
1055
|
+
end
|
|
1056
|
+
@op = :'*'
|
|
1057
|
+
@lexpr = nil
|
|
1058
|
+
precompile_inner(compiler, scope)
|
|
1059
|
+
when :'?:'
|
|
1060
|
+
# cannot precompile in place, a conditionnal expression may have a coma: must turn into If
|
|
1061
|
+
if @lexpr.kind_of? CExpression
|
|
1062
|
+
@lexpr = @lexpr.precompile_inner(compiler, scope)
|
|
1063
|
+
if not @lexpr.lexpr and not @lexpr.op and @lexpr.rexpr.kind_of? ::Numeric
|
|
1064
|
+
if @lexpr.rexpr == 0
|
|
1065
|
+
e = @rexpr[1]
|
|
1066
|
+
else
|
|
1067
|
+
e = @rexpr[0]
|
|
1068
|
+
end
|
|
1069
|
+
e = CExpression.new(nil, nil, e, e.type) if not e.kind_of? CExpression
|
|
1070
|
+
return e.precompile_inner(compiler, scope)
|
|
1071
|
+
end
|
|
1072
|
+
end
|
|
1073
|
+
raise 'conditional in toplevel' if scope == compiler.toplevel # just in case
|
|
1074
|
+
var = Variable.new
|
|
1075
|
+
var.storage = :register
|
|
1076
|
+
var.name = compiler.new_label('ternary')
|
|
1077
|
+
var.type = @rexpr[0].type
|
|
1078
|
+
CExpression.precompile_type(compiler, scope, var)
|
|
1079
|
+
Declaration.new(var).precompile(compiler, scope)
|
|
1080
|
+
If.new(@lexpr, CExpression.new(var, :'=', @rexpr[0], var.type), CExpression.new(var, :'=', @rexpr[1], var.type)).precompile(compiler, scope)
|
|
1081
|
+
@lexpr = nil
|
|
1082
|
+
@op = nil
|
|
1083
|
+
@rexpr = var
|
|
1084
|
+
precompile_inner(compiler, scope)
|
|
1085
|
+
when :'&&'
|
|
1086
|
+
if scope == compiler.toplevel
|
|
1087
|
+
@lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
|
|
1088
|
+
@rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1089
|
+
CExpression.precompile_type(compiler, scope, self)
|
|
1090
|
+
self
|
|
1091
|
+
else
|
|
1092
|
+
var = Variable.new
|
|
1093
|
+
var.storage = :register
|
|
1094
|
+
var.name = compiler.new_label('and')
|
|
1095
|
+
var.type = @type
|
|
1096
|
+
CExpression.precompile_type(compiler, scope, var)
|
|
1097
|
+
var.initializer = CExpression.new(nil, nil, 0, var.type)
|
|
1098
|
+
Declaration.new(var).precompile(compiler, scope)
|
|
1099
|
+
l = @lexpr.kind_of?(CExpression) ? @lexpr : CExpression.new(nil, nil, @lexpr, @lexpr.type)
|
|
1100
|
+
r = @rexpr.kind_of?(CExpression) ? @rexpr : CExpression.new(nil, nil, @rexpr, @rexpr.type)
|
|
1101
|
+
If.new(l, If.new(r, CExpression.new(var, :'=', CExpression.new(nil, nil, 1, var.type), var.type))).precompile(compiler, scope)
|
|
1102
|
+
@lexpr = nil
|
|
1103
|
+
@op = nil
|
|
1104
|
+
@rexpr = var
|
|
1105
|
+
precompile_inner(compiler, scope)
|
|
1106
|
+
end
|
|
1107
|
+
when :'||'
|
|
1108
|
+
if scope == compiler.toplevel
|
|
1109
|
+
@lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
|
|
1110
|
+
@rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1111
|
+
CExpression.precompile_type(compiler, scope, self)
|
|
1112
|
+
self
|
|
1113
|
+
else
|
|
1114
|
+
var = Variable.new
|
|
1115
|
+
var.storage = :register
|
|
1116
|
+
var.name = compiler.new_label('or')
|
|
1117
|
+
var.type = @type
|
|
1118
|
+
CExpression.precompile_type(compiler, scope, var)
|
|
1119
|
+
var.initializer = CExpression.new(nil, nil, 1, var.type)
|
|
1120
|
+
Declaration.new(var).precompile(compiler, scope)
|
|
1121
|
+
l = @lexpr.kind_of?(CExpression) ? @lexpr : CExpression.new(nil, nil, @lexpr, @lexpr.type)
|
|
1122
|
+
l = CExpression.new(nil, :'!', l, var.type)
|
|
1123
|
+
r = @rexpr.kind_of?(CExpression) ? @rexpr : CExpression.new(nil, nil, @rexpr, @rexpr.type)
|
|
1124
|
+
r = CExpression.new(nil, :'!', r, var.type)
|
|
1125
|
+
If.new(l, If.new(r, CExpression.new(var, :'=', CExpression.new(nil, nil, 0, var.type), var.type))).precompile(compiler, scope)
|
|
1126
|
+
@lexpr = nil
|
|
1127
|
+
@op = nil
|
|
1128
|
+
@rexpr = var
|
|
1129
|
+
precompile_inner(compiler, scope)
|
|
1130
|
+
end
|
|
1131
|
+
when :funcall
|
|
1132
|
+
if @lexpr.kind_of? Variable and @lexpr.type.kind_of? Function and @lexpr.attributes and @lexpr.attributes.include? 'inline' and @lexpr.initializer
|
|
1133
|
+
# TODO check recursive call (direct or indirect)
|
|
1134
|
+
raise 'inline varargs unsupported' if @lexpr.type.varargs
|
|
1135
|
+
rtype = @lexpr.type.type.untypedef
|
|
1136
|
+
if not rtype.kind_of? BaseType or rtype.name != :void
|
|
1137
|
+
rval = Variable.new
|
|
1138
|
+
rval.name = compiler.new_label('inline_return')
|
|
1139
|
+
rval.type = @lexpr.type.type
|
|
1140
|
+
Declaration.new(rval).precompile(compiler, scope)
|
|
1141
|
+
end
|
|
1142
|
+
inline_label = {}
|
|
1143
|
+
locals = @lexpr.type.args.zip(@rexpr).inject({}) { |h, (fa, a)|
|
|
1144
|
+
h.update fa => CExpression.new(nil, nil, a, fa.type).precompile_inner(compiler, scope)
|
|
1145
|
+
}
|
|
1146
|
+
copy_inline_ce = lambda { |ce|
|
|
1147
|
+
case ce
|
|
1148
|
+
when CExpression; CExpression.new(copy_inline_ce[ce.lexpr], ce.op, copy_inline_ce[ce.rexpr], ce.type)
|
|
1149
|
+
when Variable; locals[ce] || ce
|
|
1150
|
+
when ::Array; ce.map { |e_| copy_inline_ce[e_] }
|
|
1151
|
+
else ce
|
|
1152
|
+
end
|
|
1153
|
+
}
|
|
1154
|
+
copy_inline = lambda { |stmt, scp|
|
|
1155
|
+
case stmt
|
|
1156
|
+
when Block
|
|
1157
|
+
b = Block.new(scp)
|
|
1158
|
+
stmt.statements.each { |s|
|
|
1159
|
+
s = copy_inline[s, b]
|
|
1160
|
+
b.statements << s if s
|
|
1161
|
+
}
|
|
1162
|
+
b
|
|
1163
|
+
when If; If.new(copy_inline_ce[stmt.test], copy_inline[stmt.bthen, scp]) # re-precompile ?
|
|
1164
|
+
when Label; Label.new(inline_label[stmt.name] ||= compiler.new_label('inline_'+stmt.name))
|
|
1165
|
+
when Goto; Goto.new(inline_label[stmt.target] ||= compiler.new_label('inline_'+stmt.target))
|
|
1166
|
+
when Return; CExpression.new(rval, :'=', copy_inline_ce[stmt.value], rval.type).precompile_inner(compiler, scp) if stmt.value
|
|
1167
|
+
when CExpression; copy_inline_ce[stmt]
|
|
1168
|
+
when Declaration
|
|
1169
|
+
nv = stmt.var.dup
|
|
1170
|
+
if nv.type.kind_of? Array and nv.type.length.kind_of? CExpression
|
|
1171
|
+
nv.type = Array.new(nv.type.type, copy_inline_ce[nv.type.length]) # XXX nested dynamic?
|
|
1172
|
+
end
|
|
1173
|
+
locals[stmt.var] = nv
|
|
1174
|
+
scp.symbol[nv.name] = nv
|
|
1175
|
+
Declaration.new(nv)
|
|
1176
|
+
else raise 'unexpected inline statement ' + stmt.inspect
|
|
1177
|
+
end
|
|
1178
|
+
}
|
|
1179
|
+
scope.statements << copy_inline[@lexpr.initializer, scope] # body already precompiled
|
|
1180
|
+
CExpression.new(nil, nil, rval, rval.type).precompile_inner(compiler, scope)
|
|
1181
|
+
elsif @type.kind_of? Union
|
|
1182
|
+
var = Variable.new
|
|
1183
|
+
var.name = compiler.new_label('return_struct')
|
|
1184
|
+
var.type = @type
|
|
1185
|
+
Declaration.new(var).precompile(compiler, scope)
|
|
1186
|
+
@rexpr.unshift CExpression.new(nil, :&, var, Pointer.new(var.type))
|
|
1187
|
+
|
|
1188
|
+
var2 = Variable.new
|
|
1189
|
+
var2.name = compiler.new_label('return_struct_ptr')
|
|
1190
|
+
var2.type = Pointer.new(@type)
|
|
1191
|
+
var2.storage = :register
|
|
1192
|
+
CExpression.precompile_type(compiler, scope, var2)
|
|
1193
|
+
Declaration.new(var2).precompile(compiler, scope)
|
|
1194
|
+
@type = var2.type
|
|
1195
|
+
CExpression.new(var2, :'=', self, var2.type).precompile(compiler, scope)
|
|
1196
|
+
|
|
1197
|
+
CExpression.new(nil, :'*', var2, var.type).precompile_inner(compiler, scope)
|
|
1198
|
+
else
|
|
1199
|
+
t = @lexpr.type.untypedef
|
|
1200
|
+
t = t.type.untypedef if t.pointer?
|
|
1201
|
+
@lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
|
|
1202
|
+
types = t.args.map { |a| a.type }
|
|
1203
|
+
# cast args to func prototype
|
|
1204
|
+
@rexpr.map! { |e_| (types.empty? ? e_ : CExpression.new(nil, nil, e_, types.shift)).precompile_inner(compiler, scope) }
|
|
1205
|
+
CExpression.precompile_type(compiler, scope, self)
|
|
1206
|
+
self
|
|
1207
|
+
end
|
|
1208
|
+
when :','
|
|
1209
|
+
lexpr = @lexpr.kind_of?(CExpression) ? @lexpr : CExpression.new(nil, nil, @lexpr, @lexpr.type)
|
|
1210
|
+
rexpr = @rexpr.kind_of?(CExpression) ? @rexpr : CExpression.new(nil, nil, @rexpr, @rexpr.type)
|
|
1211
|
+
lexpr.precompile(compiler, scope)
|
|
1212
|
+
rexpr.precompile_inner(compiler, scope)
|
|
1213
|
+
when :'!'
|
|
1214
|
+
CExpression.precompile_type(compiler, scope, self)
|
|
1215
|
+
if @rexpr.kind_of?(CExpression)
|
|
1216
|
+
case @rexpr.op
|
|
1217
|
+
when :'<', :'>', :'<=', :'>=', :'==', :'!='
|
|
1218
|
+
@op = { :'<' => :'>=', :'>' => :'<=', :'<=' => :'>', :'>=' => :'<',
|
|
1219
|
+
:'==' => :'!=', :'!=' => :'==' }[@rexpr.op]
|
|
1220
|
+
@lexpr = @rexpr.lexpr
|
|
1221
|
+
@rexpr = @rexpr.rexpr
|
|
1222
|
+
precompile_inner(compiler, scope)
|
|
1223
|
+
when :'&&', :'||'
|
|
1224
|
+
@op = { :'&&' => :'||', :'||' => :'&&' }[@rexpr.op]
|
|
1225
|
+
@lexpr = CExpression.new(nil, :'!', @rexpr.lexpr, @type)
|
|
1226
|
+
@rexpr = CExpression.new(nil, :'!', @rexpr.rexpr, @type)
|
|
1227
|
+
precompile_inner(compiler, scope)
|
|
1228
|
+
when :'!'
|
|
1229
|
+
if @rexpr.rexpr.kind_of? CExpression
|
|
1230
|
+
@op = nil
|
|
1231
|
+
@rexpr = @rexpr.rexpr
|
|
1232
|
+
else
|
|
1233
|
+
@op = :'!='
|
|
1234
|
+
@lexpr = @rexpr.rexpr
|
|
1235
|
+
@rexpr = CExpression.new(nil, nil, 0, @lexpr.type)
|
|
1236
|
+
end
|
|
1237
|
+
precompile_inner(compiler, scope)
|
|
1238
|
+
else
|
|
1239
|
+
@rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1240
|
+
self
|
|
1241
|
+
end
|
|
1242
|
+
else
|
|
1243
|
+
@rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1244
|
+
self
|
|
1245
|
+
end
|
|
1246
|
+
when :'++', :'--'
|
|
1247
|
+
if not @rexpr
|
|
1248
|
+
var = Variable.new
|
|
1249
|
+
var.storage = :register
|
|
1250
|
+
var.name = compiler.new_label('postincrement')
|
|
1251
|
+
var.type = @type
|
|
1252
|
+
Declaration.new(var).precompile(compiler, scope)
|
|
1253
|
+
CExpression.new(var, :'=', @lexpr, @type).precompile(compiler, scope)
|
|
1254
|
+
CExpression.new(nil, @op, @lexpr, @type).precompile(compiler, scope)
|
|
1255
|
+
@lexpr = nil
|
|
1256
|
+
@op = nil
|
|
1257
|
+
@rexpr = var
|
|
1258
|
+
precompile_inner(compiler, scope)
|
|
1259
|
+
elsif @type.pointer? and compiler.sizeof(nil, @type.untypedef.type.untypedef) != 1
|
|
1260
|
+
# ++ptr => ptr += sizeof(*ptr) (done in += precompiler)
|
|
1261
|
+
@op = { :'++' => :'+=', :'--' => :'-=' }[@op]
|
|
1262
|
+
@lexpr = @rexpr
|
|
1263
|
+
@rexpr = CExpression.new(nil, nil, 1, BaseType.new(:ptr, :unsigned))
|
|
1264
|
+
precompile_inner(compiler, scope)
|
|
1265
|
+
else
|
|
1266
|
+
CExpression.precompile_type(compiler, scope, self)
|
|
1267
|
+
@rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1268
|
+
self
|
|
1269
|
+
end
|
|
1270
|
+
when :'='
|
|
1271
|
+
# handle structure assignment/array assignment
|
|
1272
|
+
case @lexpr.type.untypedef
|
|
1273
|
+
when Union
|
|
1274
|
+
# rexpr may be a :funcall
|
|
1275
|
+
@rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1276
|
+
@lexpr.type.untypedef.members.zip(@rexpr.type.untypedef.members) { |m1, m2|
|
|
1277
|
+
# assume m1 and m2 are compatible
|
|
1278
|
+
v1 = CExpression.new(@lexpr, :'.', m1.name, m1.type)
|
|
1279
|
+
v2 = CExpression.new(@rexpr, :'.', m2.name, m1.type)
|
|
1280
|
+
CExpression.new(v1, :'=', v2, v1.type).precompile(compiler, scope)
|
|
1281
|
+
}
|
|
1282
|
+
# (foo = bar).toto
|
|
1283
|
+
@op = nil
|
|
1284
|
+
@rexpr = @lexpr
|
|
1285
|
+
@lexpr = nil
|
|
1286
|
+
@type = @rexpr.type
|
|
1287
|
+
precompile_inner(compiler, scope) if nested
|
|
1288
|
+
when Array
|
|
1289
|
+
if not len = @lexpr.type.untypedef.length
|
|
1290
|
+
@rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1291
|
+
# char toto[] = "bla"
|
|
1292
|
+
if @rexpr.kind_of? CExpression and not @rexpr.lexpr and not @rexpr.op and
|
|
1293
|
+
@rexpr.rexpr.kind_of? Variable and @rexpr.rexpr.type.kind_of? Array
|
|
1294
|
+
len = @rexpr.rexpr.type.length
|
|
1295
|
+
end
|
|
1296
|
+
end
|
|
1297
|
+
raise 'array initializer with no length !' if not len
|
|
1298
|
+
# TODO optimize...
|
|
1299
|
+
len.times { |i|
|
|
1300
|
+
i = CExpression.new(nil, nil, i, BaseType.new(:long, :unsigned))
|
|
1301
|
+
v1 = CExpression.new(@lexpr, :'[]', i, @lexpr.type.untypedef.type)
|
|
1302
|
+
v2 = CExpression.new(@rexpr, :'[]', i, v1.type)
|
|
1303
|
+
CExpression.new(v1, :'=', v2, v1.type).precompile(compiler, scope)
|
|
1304
|
+
}
|
|
1305
|
+
@op = nil
|
|
1306
|
+
@rexpr = @lexpr
|
|
1307
|
+
@lexpr = nil
|
|
1308
|
+
@type = @rexpr.type
|
|
1309
|
+
precompile_inner(compiler, scope) if nested
|
|
1310
|
+
else
|
|
1311
|
+
@lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
|
|
1312
|
+
@rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1313
|
+
CExpression.precompile_type(compiler, scope, self)
|
|
1314
|
+
self
|
|
1315
|
+
end
|
|
1316
|
+
when nil
|
|
1317
|
+
case @rexpr
|
|
1318
|
+
when Block
|
|
1319
|
+
# compound statements
|
|
1320
|
+
raise 'compound statement in toplevel' if scope == compiler.toplevel # just in case
|
|
1321
|
+
var = Variable.new
|
|
1322
|
+
var.storage = :register
|
|
1323
|
+
var.name = compiler.new_label('compoundstatement')
|
|
1324
|
+
var.type = @type
|
|
1325
|
+
CExpression.precompile_type(compiler, scope, var)
|
|
1326
|
+
Declaration.new(var).precompile(compiler, scope)
|
|
1327
|
+
if @rexpr.statements.last.kind_of? CExpression
|
|
1328
|
+
@rexpr.statements[-1] = CExpression.new(var, :'=', @rexpr.statements[-1], var.type)
|
|
1329
|
+
@rexpr.precompile(compiler, scope)
|
|
1330
|
+
end
|
|
1331
|
+
@rexpr = var
|
|
1332
|
+
precompile_inner(compiler, scope)
|
|
1333
|
+
when ::String
|
|
1334
|
+
# char[] immediate
|
|
1335
|
+
v = Variable.new
|
|
1336
|
+
v.storage = :static
|
|
1337
|
+
v.name = 'char_' + @rexpr.tr('^a-zA-Z', '')[0, 8]
|
|
1338
|
+
v.type = Array.new(@type.type)
|
|
1339
|
+
v.type.length = @rexpr.length + 1
|
|
1340
|
+
v.type.type.qualifier = [:const]
|
|
1341
|
+
v.initializer = CExpression.new(nil, nil, @rexpr, @type)
|
|
1342
|
+
Declaration.new(v).precompile(compiler, scope)
|
|
1343
|
+
@rexpr = v
|
|
1344
|
+
precompile_inner(compiler, scope)
|
|
1345
|
+
when ::Float
|
|
1346
|
+
# float immediate
|
|
1347
|
+
v = Variable.new
|
|
1348
|
+
v.storage = :static
|
|
1349
|
+
v.name = @type.untypedef.name.to_s
|
|
1350
|
+
v.type = @type
|
|
1351
|
+
v.type.qualifier = [:const]
|
|
1352
|
+
v.initializer = CExpression.new(nil, nil, @rexpr, @type)
|
|
1353
|
+
Declaration.new(v).precompile(compiler, scope)
|
|
1354
|
+
@rexpr = CExpression.new(nil, :'*', v, v.type)
|
|
1355
|
+
precompile_inner(compiler, scope)
|
|
1356
|
+
when CExpression
|
|
1357
|
+
# simplify casts
|
|
1358
|
+
CExpression.precompile_type(compiler, scope, self)
|
|
1359
|
+
# propagate type first so that __uint64 foo() { return -1 } => 0xffffffffffffffff
|
|
1360
|
+
@rexpr.type = @type if @rexpr.kind_of? CExpression and @rexpr.op == :- and not @rexpr.lexpr and @type.kind_of? BaseType and @type.name == :__int64 # XXX kill me
|
|
1361
|
+
@rexpr = @rexpr.precompile_inner(compiler, scope)
|
|
1362
|
+
if @type.kind_of? BaseType and @rexpr.type.kind_of? BaseType
|
|
1363
|
+
if @rexpr.type == @type
|
|
1364
|
+
# noop cast
|
|
1365
|
+
@lexpr, @op, @rexpr = @rexpr.lexpr, @rexpr.op, @rexpr.rexpr
|
|
1366
|
+
elsif not @rexpr.op and @type.integral? and @rexpr.type.integral?
|
|
1367
|
+
if @rexpr.rexpr.kind_of? ::Numeric and (val = reduce(compiler)).kind_of? ::Numeric
|
|
1368
|
+
@rexpr = val
|
|
1369
|
+
elsif compiler.typesize[@type.name] < compiler.typesize[@rexpr.type.name]
|
|
1370
|
+
# (char)(short)(int)(long)foo => (char)foo
|
|
1371
|
+
@rexpr = @rexpr.rexpr
|
|
1372
|
+
end
|
|
1373
|
+
end
|
|
1374
|
+
end
|
|
1375
|
+
self
|
|
1376
|
+
else
|
|
1377
|
+
CExpression.precompile_type(compiler, scope, self)
|
|
1378
|
+
self
|
|
1379
|
+
end
|
|
1380
|
+
else
|
|
1381
|
+
# int+ptr => ptr+int
|
|
1382
|
+
if @op == :+ and @lexpr and @lexpr.type.integral? and @rexpr.type.pointer?
|
|
1383
|
+
@rexpr, @lexpr = @lexpr, @rexpr
|
|
1384
|
+
end
|
|
1385
|
+
|
|
1386
|
+
# handle pointer + 2 == ((char *)pointer) + 2*sizeof(*pointer)
|
|
1387
|
+
if @rexpr and [:'+', :'+=', :'-', :'-='].include? @op and
|
|
1388
|
+
@type.pointer? and @rexpr.type.integral?
|
|
1389
|
+
sz = compiler.sizeof(nil, @type.untypedef.type.untypedef)
|
|
1390
|
+
if sz != 1
|
|
1391
|
+
sz = CExpression.new(nil, nil, sz, @rexpr.type)
|
|
1392
|
+
@rexpr = CExpression.new(@rexpr, :'*', sz, @rexpr.type)
|
|
1393
|
+
end
|
|
1394
|
+
end
|
|
1395
|
+
|
|
1396
|
+
# type promotion => cast
|
|
1397
|
+
case @op
|
|
1398
|
+
when :+, :-, :*, :/, :&, :|, :^, :%
|
|
1399
|
+
if @lexpr
|
|
1400
|
+
if @lexpr.type != @type
|
|
1401
|
+
@lexpr = CExpression.new(nil, nil, @lexpr, @lexpr.type) if not @lexpr.kind_of? CExpression
|
|
1402
|
+
@lexpr = CExpression.new(nil, nil, @lexpr, @type)
|
|
1403
|
+
end
|
|
1404
|
+
if @rexpr.type != @type
|
|
1405
|
+
@rexpr = CExpression.new(nil, nil, @rexpr, @rexpr.type) if not @rexpr.kind_of? CExpression
|
|
1406
|
+
@rexpr = CExpression.new(nil, nil, @rexpr, @type)
|
|
1407
|
+
end
|
|
1408
|
+
end
|
|
1409
|
+
when :>>, :<<
|
|
1410
|
+
# char => int
|
|
1411
|
+
if @lexpr.type != @type
|
|
1412
|
+
@lexpr = CExpression.new(nil, nil, @lexpr, @lexpr.type) if not @lexpr.kind_of? CExpression
|
|
1413
|
+
@lexpr = CExpression.new(nil, nil, @lexpr, @type)
|
|
1414
|
+
end
|
|
1415
|
+
when :'+=', :'-=', :'*=', :'/=', :'&=', :'|=', :'^=', :'%='
|
|
1416
|
+
if @rexpr.type != @lexpr.type
|
|
1417
|
+
@rexpr = CExpression.new(nil, nil, @rexpr, @rexpr.type) if not @rexpr.kind_of? CExpression
|
|
1418
|
+
@rexpr = CExpression.new(nil, nil, @rexpr, @type)
|
|
1419
|
+
end
|
|
1420
|
+
end
|
|
1421
|
+
|
|
1422
|
+
@lexpr = CExpression.precompile_inner(compiler, scope, @lexpr)
|
|
1423
|
+
@rexpr = CExpression.precompile_inner(compiler, scope, @rexpr)
|
|
1424
|
+
|
|
1425
|
+
if @op == :'&' and not @lexpr
|
|
1426
|
+
rr = @rexpr
|
|
1427
|
+
rr = rr.rexpr while rr.kind_of? CExpression and not rr.op
|
|
1428
|
+
if rr.kind_of? CExpression and rr.op == :'*' and not rr.lexpr
|
|
1429
|
+
@lexpr = nil
|
|
1430
|
+
@op = nil
|
|
1431
|
+
@rexpr = rr.rexpr
|
|
1432
|
+
return precompile_inner(compiler, scope)
|
|
1433
|
+
elsif rr != @rexpr
|
|
1434
|
+
@rexpr = rr
|
|
1435
|
+
return precompile_inner(compiler, scope)
|
|
1436
|
+
end
|
|
1437
|
+
end
|
|
1438
|
+
|
|
1439
|
+
CExpression.precompile_type(compiler, scope, self)
|
|
1440
|
+
|
|
1441
|
+
isnumeric = lambda { |e_| e_.kind_of?(::Numeric) or (e_.kind_of? CExpression and
|
|
1442
|
+
not e_.lexpr and not e_.op and e_.rexpr.kind_of? ::Numeric) }
|
|
1443
|
+
|
|
1444
|
+
# calc numeric
|
|
1445
|
+
# XXX do not simplify operations involving variables (for type overflow etc)
|
|
1446
|
+
if isnumeric[@rexpr] and (not @lexpr or isnumeric[@lexpr]) and (val = reduce(compiler)).kind_of? ::Numeric
|
|
1447
|
+
@lexpr = nil
|
|
1448
|
+
@op = nil
|
|
1449
|
+
@rexpr = val
|
|
1450
|
+
end
|
|
1451
|
+
|
|
1452
|
+
self
|
|
1453
|
+
end
|
|
1454
|
+
end
|
|
1455
|
+
end
|
|
1456
|
+
end
|
|
1457
|
+
end
|