asmcc 0.4.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 (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/asmcc +4 -0
  3. data/lib/asmcc.rb +445 -0
  4. metadata +49 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9bb8677cf4a4a7825e28b603b32a032a75a805f6
4
+ data.tar.gz: b77f28755ad622294173b9a4fc50b0b3d2e8d2ff
5
+ SHA512:
6
+ metadata.gz: 6871ff27f02b4ffdf139715951af599a85357e5cea46495285705bf8f7fe1946c9c80b0bb03c1ddd21275a6ce30929e54e7e50995806d24e4f5066d474a9a6dd
7
+ data.tar.gz: c0cc1fe622d6b82e6cc55dccd908d033702a5733dddf28c8b4f9bde083dfd47b7bbc7ca2ef915f685bb5d9f95b40d1dbed27d486c7518c2d2051fd2cb47d8334
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'asmcc'
3
+
4
+ AsmCC.new.run
@@ -0,0 +1,445 @@
1
+ require 'optparse'
2
+ require 'tempfile'
3
+
4
+ CXX_TEMPLATE = <<-CODE_EOF
5
+ #include <cinttypes>
6
+ #include <cmath>
7
+ #include <cstddef>
8
+ #include <cstring>
9
+ #include <climits>
10
+
11
+ uint64_t factorial(uint64_t arg) {
12
+ if (arg <= 1) return 1;
13
+ return arg * factorial(arg - 1);
14
+ }
15
+ CODE_EOF
16
+
17
+
18
+ C_TEMPLATE = <<-CODE_EOF
19
+ #include <inttypes.h>
20
+ #include <math.h>
21
+ #include <stddef.h>
22
+ #include <string.h>
23
+ #include <limits.h>
24
+ #include <stdbool.h>
25
+
26
+ uint64_t factorial(uint64_t arg) {
27
+ if (arg <= 1) return 1;
28
+ return arg * factorial(arg - 1);
29
+ }
30
+ CODE_EOF
31
+
32
+
33
+ class AsmCC
34
+ def initialize
35
+ @parsed_settings = false
36
+ @custom_template = nil
37
+ @warnings = %W[all effc++]
38
+ @includes = %W[#{Dir.pwd} #{Dir.pwd}/include /usr/local/include]
39
+ @opt_level = '3'
40
+
41
+ @input = nil
42
+ @output = nil
43
+
44
+ @defines = []
45
+ @undefs = []
46
+
47
+ @enable_exns = false
48
+ @enable_rtti = false
49
+
50
+ @accurate_fp = true
51
+
52
+ @output = nil
53
+
54
+ @arch = 'native'
55
+
56
+ @xflags = []
57
+
58
+ @force_bits = nil
59
+
60
+ @verbose_asm = true
61
+
62
+ @edit_asm = false
63
+ @show_encoding = false
64
+ @debug = false
65
+ @demangle = true
66
+ @emit_llvm = false
67
+
68
+ @combined_output = false
69
+
70
+ @std = "c++1y"
71
+ @lang = :"c++"
72
+ end
73
+
74
+ def parse_settings!
75
+ return self if @parsed_settings
76
+ @parsed_settings = true
77
+ OptionParser.new do |opts|
78
+ opts.banner = 'Usage: asmcc [options]'
79
+ opts.separator ''
80
+ opts.separator 'Options:'
81
+
82
+ opts.on('-g', '--debug-info', 'Include debugging info?') do
83
+ @debug = true
84
+ end
85
+
86
+ opts.on('-O', '--opt-level 0123s', '01234s'.split(''), 'Set optimization level (3 by default).') do |v|
87
+ @opt_level = v
88
+ end
89
+
90
+ opts.on('-l', '--lang LANG', [:"c++", :c, :"objective-c", :"objective-c++"],
91
+ 'Set language. Defaults to c++.',
92
+ ' Must be one of ["c++", "c", "objective-c", "objective-c++"]') do |l|
93
+ @lang = l
94
+ end
95
+
96
+ opts.on('-s', '--std STD', String, 'Use the specified standard. Defaults to c++1y for c++/objc++, c11 for c/objc.') do |std|
97
+ @std = std
98
+ end
99
+
100
+ opts.on('-L', '--emit-llvm', 'Emit LLVM IR. (disables --show-encoding).') do |v|
101
+ @emit_llvm = true
102
+ end
103
+
104
+ opts.on('-F', '--[no-]fast-math', 'Prefer speed to accuracy for floating point? (off by default).') do |b|
105
+ @accurate_fp = !b
106
+ end
107
+
108
+ opts.on('-a', '--arch ARCH', String, 'Specify -march flag value (default is "native").') do |arch|
109
+ @arch = arch
110
+ end
111
+
112
+ opts.on('--m32', 'Force 32 bit compilation.') do
113
+ @force_bits = 32
114
+ end
115
+
116
+ opts.on('--m64', 'Force 64 bit compilation.') do
117
+ @force_bits = 64
118
+ end
119
+
120
+ opts.on('-X', '--Xcc f,l,a,g,s', Array, 'Pass extra flags to the compiler.') do |fs|
121
+ @xflags += fs
122
+ end
123
+
124
+ opts.on('-e', '--[no-]exceptions', 'Enable exceptions. Disabled by default.') do |e|
125
+ @enable_exns = e
126
+ end
127
+
128
+ opts.on('-r', '--[no-]rtti', 'Enable runtime type info. Disabled by default.') do |r|
129
+ @enable_rtti = r
130
+ end
131
+
132
+ opts.on('-E', '--edit-result', 'Open output file in editor.') do
133
+ @edit_asm = true
134
+ end
135
+
136
+ opts.on('--no-verbose-asm', 'Disable verbose assembly output.') do
137
+ @verbose_asm = false
138
+ end
139
+
140
+ opts.on('-D', '--define l,i,s,t', Array, 'Pass -Dl -Di -Ds -Dt to the compiler.') do |ds|
141
+ @defines += ds
142
+ end
143
+
144
+ opts.on('-U', '--undef l,i,s,t', Array, 'Pass -Ul -Ui -Us -Ut to the compiler (overrides -D).') do |us|
145
+ @undefs += us
146
+ end
147
+
148
+ opts.on('-W', '--warn w0,w1,etc', Array, 'Specify warnings to use (defaults to Wall,Wextra).') do |ws|
149
+ @warnings = ws
150
+ end
151
+
152
+ opts.on('-I', '--include d,i,r,s', Array, 'Extra -I include directories.',
153
+ ' Automatically contains "`pwd`,`pwd`/include,/usr/local/include".') do |is|
154
+ @includes += is
155
+ end
156
+
157
+ opts.on('-S', '--show-encoding', 'Show encoding?') do
158
+ @show_encoding = true
159
+ end
160
+
161
+ opts.on('-C', '--combined-output', 'Output both the source fed into the compiler and the generated assembly') do
162
+ @combined_output = true
163
+ end
164
+
165
+ opts.on('-o', '--out FILE', 'Output to FILE instead of stdout.') do |f|
166
+ @output = f
167
+ end
168
+
169
+ opts.on('-i', '--input FILE', 'Use FILE as input instead of opening from the editor.') do |f|
170
+ if File.exist? f
171
+ @input = f
172
+ else
173
+ abort "No such file: \"#{f}\""
174
+ end
175
+ end
176
+
177
+ opts.on('-T', '--template FILE', String, 'Use FILE for template') do |f|
178
+ begin
179
+ @custom_template = IO.read(f)
180
+ rescue
181
+ puts "Warning: Unable to read template file: \"#{f}\", ignoring -T option..."
182
+ @custom_template = nil
183
+ end
184
+ end
185
+
186
+ opts.on('--[no-]demangle', 'Demangle C++ identifiers (requires `c++filt`, ignored for c and objc)') do |d|
187
+ @demangle = false
188
+ end
189
+
190
+ opts.on('-v', '--verbose', 'Pass -v to the compiler') do
191
+ @xflags << '-v'
192
+ end
193
+
194
+ opts.on('-h', '--help', 'Show this message') do
195
+ puts opts
196
+ exit
197
+ end
198
+
199
+ end.parse!
200
+
201
+ @show_encoding = false if @emit_llvm
202
+
203
+ unless is_cxx?
204
+ if @std.include? '++'
205
+ @std = @std.include?('gnu') ? 'gnu11' : 'c11'
206
+ end
207
+ end
208
+
209
+ self
210
+ end
211
+
212
+ def try_again? prompt, default
213
+ print "#{prompt} #{default ? '[Y/n]' : '[y/N]'}:"
214
+ result = gets.strip
215
+ if result.empty?
216
+ default
217
+ elsif result[0] == 'Y' || result[0] == 'y'
218
+ true
219
+ elsif result[0] == 'N' || result[0] == 'n'
220
+ false
221
+ else
222
+ default
223
+ end
224
+ end
225
+
226
+ def edit_file fpath
227
+ system(ENV['VISUAL'] || ENV['EDITOR'] || '/usr/bin/nano', fpath)
228
+ end
229
+
230
+ def is_darwin?
231
+ `uname`.strip == 'Darwin'
232
+ end
233
+
234
+ def is_cxx?
235
+ @lang == :"c++" or @lang == :"objective-c++"
236
+ end
237
+
238
+ def is_objc?
239
+ @lang == :"objective-c" or @lang == :"objective-c++"
240
+ end
241
+
242
+ def file_extension
243
+ case @lang
244
+ when :"c"
245
+ ".c"
246
+ when :"c++"
247
+ ".cpp"
248
+ when :"objective-c"
249
+ ".m"
250
+ when :"objective-cpp"
251
+ ".mm"
252
+ end
253
+ end
254
+
255
+ def stdlib_flag
256
+ if is_cxx?
257
+ is_darwin? ? '-stdlib=libc++' : '-stdlib=libstdc++'
258
+ else
259
+ ''
260
+ end
261
+ end
262
+
263
+ def exception_flag
264
+ if is_cxx?
265
+ if @enable_exns
266
+ "-fexceptions" + (is_objc? ? " -fobj-exceptions" : "")
267
+ else
268
+ "-fno-exceptions" + (is_objc? ? " -fno-objc-exceptions" : "")
269
+ end
270
+ else
271
+ ''
272
+ end
273
+ end
274
+
275
+ def rtti_flag
276
+ if is_cxx?
277
+ @enable_rtti ? '-frtti' : '-fno-rtti'
278
+ else
279
+ ''
280
+ end
281
+ end
282
+
283
+ def flags
284
+ %W[
285
+ -x#{@lang}
286
+ -march=#{@arch}
287
+ #{@force_bits.nil? ? '' : "-m#{@force_bits}"}
288
+ -S
289
+ #{@emit_llvm ? '-emit-llvm': ''}
290
+ #{@verbose_asm ? '-Xclang -masm-verbose' : ''}
291
+
292
+ -std=#{@std}
293
+ #{stdlib_flag}
294
+ -O#{@opt_level.strip}
295
+
296
+ #{exception_flag}
297
+
298
+ #{rtti_flag}
299
+
300
+ #{@accurate_fp ? '' : '-ffast-math'}
301
+
302
+ #{@warnings.map{|s| to_warning s}.join ' '}
303
+ #{@includes.map{|s| to_include s}.join ' '}
304
+
305
+ #{@defines.reject(&:empty?).map{|d|"-D#{d.strip}"}.join ' '}
306
+ #{@undefs.reject(&:empty?).map{|u|"-U#{d.strip}"}.join ' '}
307
+
308
+ #{@debug ? '-g' : ''}
309
+ #{@xflags.join ' '}
310
+ ].reject(&:empty?)
311
+ end
312
+
313
+ def flag_str
314
+ flags.join ' '
315
+ end
316
+
317
+ def to_warning(s)
318
+ s.strip.sub /^(?:-?W)?/, "-W"
319
+ end
320
+
321
+ def to_include(s)
322
+ s.strip.sub /^(?:-I)?/, "-I"
323
+ end
324
+
325
+ def flags_summary
326
+ flags.reject{|f| /^-[WI]/ =~ f}.join ' '
327
+ end
328
+
329
+ def should_demangle
330
+ @demangle and is_cxx?()
331
+ end
332
+
333
+ def compiler_cmd path, summarize
334
+ command = "clang " + (summarize ? flags_summary : "#{flag_str} #{path} -o '-'")
335
+ command += " | clang -cc1as -show-encoding" if @show_encoding
336
+ command += " | c++filt" if should_demangle
337
+ end
338
+
339
+ def compiled_with comment_str
340
+ cmd = "clang #{flags_summary} "
341
+ cmd += " | clang -cc1as -show-encoding" if @show_encoding
342
+ cmd += " | c++filt" if should_demangle
343
+ "#{comment_str} Compiled with `#{cmd}`\n"
344
+ end
345
+
346
+ def invoke_compiler path
347
+ # this is getting pretty hacky
348
+ out = IO.popen("clang #{flag_str} #{path} -o '-'") do |pipe|
349
+ pipe.read
350
+ end
351
+
352
+ unless $?.exitstatus == 0
353
+ return [false]
354
+ end
355
+
356
+ if @show_encoding
357
+ out = IO.popen('clang -cc1as -show-encoding', 'r+') do |pipe|
358
+ pipe.write out
359
+ pipe.close_write
360
+ pipe.read
361
+ end
362
+ unless $?.exitstatus == 0
363
+ return [false]
364
+ end
365
+ end
366
+
367
+ if should_demangle
368
+ out = IO.popen('c++filt', 'r+') do |pipe|
369
+ pipe.write out
370
+ pipe.close_write
371
+ pipe.read
372
+ end
373
+ unless $?.exitstatus == 0
374
+ return [false]
375
+ end
376
+ end
377
+ [true, out]
378
+ end
379
+
380
+ def template
381
+ if @custom_template
382
+ @custom_template
383
+ elsif is_cxx?
384
+ CXX_TEMPLATE
385
+ else
386
+ C_TEMPLATE
387
+ end
388
+ end
389
+
390
+ def run
391
+ parse_settings!
392
+
393
+ compiled = nil
394
+ file = nil
395
+
396
+ if @input.nil?
397
+ templ = template
398
+ else
399
+ templ = IO.read @input
400
+ end
401
+
402
+ file = Tempfile.new(['asmcc', file_extension])
403
+
404
+ path = file.path
405
+
406
+ file.print template
407
+ file.close()
408
+
409
+ compiled = nil
410
+ tried_once = false
411
+
412
+ while true
413
+ edit_file path if @input.nil? or tried_once
414
+ tried_once = true
415
+ result = invoke_compiler path
416
+ if result[0]
417
+ compiled = result[1]
418
+ break
419
+ elsif not try_again? "Compilation failed, try again?", true
420
+ exit(1)
421
+ end
422
+ end
423
+
424
+ if @edit_asm or not @output.nil?
425
+ out_path = @output.nil? ? path : @output
426
+ File.open(out_path, 'w') { |f| output f, (IO.read path), compiled }
427
+ edit_file out_path if @edit_asm
428
+ else
429
+ output $stdout, (IO.read path), compiled
430
+ end
431
+ end
432
+
433
+ def output f, source, compiled
434
+ if @combined_output
435
+ f.puts source
436
+ f.puts "\n/******* Generated code *********/"
437
+ f.puts compiled_with "/*"
438
+ else
439
+ f.puts compiled_with @emit_llvm ? ';' : '#'
440
+ end
441
+ f.puts compiled.each_line.to_a.map{|line| "#{line.gsub(/([^\t]*)(\t)/) { $1 + " " * (8 - $1.length % 8) }}"}.join
442
+ f.puts "\n*/" if @combined_output
443
+ end
444
+
445
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: asmcc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Thom Chiovoloni
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-16 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |
14
+ AsmCC is a command line tool to view the assembly or LLVM IR produced by
15
+ clang for a given file. It's essentially a command-line version of the
16
+ (now disabled) llvm online demo page.
17
+ email: chiovolonit@gmail.com
18
+ executables:
19
+ - asmcc
20
+ extensions: []
21
+ extra_rdoc_files: []
22
+ files:
23
+ - lib/asmcc.rb
24
+ - bin/asmcc
25
+ homepage: https://github.com/thomcc/asmcc
26
+ licenses:
27
+ - CC0
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.0.3
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: A script which painlessly compiles C/C++ to assembly.
49
+ test_files: []