metasm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. 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