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.
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