PageRenderTime 46ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/darwin/depends/gas-preprocessor/gas-preprocessor.pl

https://github.com/energy6/xbmc
Perl | 497 lines | 403 code | 45 blank | 49 comment | 52 complexity | 50c4f1a7914e4bd06b8854f51dd0d037 MD5 | raw file
  1. #!/usr/bin/env perl
  2. # by David Conrad
  3. # This code is licensed under GPLv2 or later; go to gnu.org to read it
  4. # (not that it much matters for an asm preprocessor)
  5. # usage: set your assembler to be something like "perl gas-preprocessor.pl gcc"
  6. use strict;
  7. # Apple's gas is ancient and doesn't support modern preprocessing features like
  8. # .rept and has ugly macro syntax, among other things. Thus, this script
  9. # implements the subset of the gas preprocessor used by x264 and ffmpeg
  10. # that isn't supported by Apple's gas.
  11. my @gcc_cmd = @ARGV;
  12. my @preprocess_c_cmd;
  13. my $fix_unreq = $^O eq "darwin";
  14. if ($gcc_cmd[0] eq "-fix-unreq") {
  15. $fix_unreq = 1;
  16. shift @gcc_cmd;
  17. } elsif ($gcc_cmd[0] eq "-no-fix-unreq") {
  18. $fix_unreq = 0;
  19. shift @gcc_cmd;
  20. }
  21. if (grep /\.c$/, @gcc_cmd) {
  22. # C file (inline asm?) - compile
  23. @preprocess_c_cmd = (@gcc_cmd, "-S");
  24. } elsif (grep /\.[sS]$/, @gcc_cmd) {
  25. # asm file, just do C preprocessor
  26. @preprocess_c_cmd = (@gcc_cmd, "-E");
  27. } else {
  28. die "Unrecognized input filetype";
  29. }
  30. # if compiling, avoid creating an output file named '-.o'
  31. if ((grep /^-c$/, @gcc_cmd) && !(grep /^-o/, @gcc_cmd)) {
  32. foreach my $i (@gcc_cmd) {
  33. if ($i =~ /\.[csS]$/) {
  34. my $outputfile = $i;
  35. $outputfile =~ s/\.[csS]$/.o/;
  36. push(@gcc_cmd, "-o");
  37. push(@gcc_cmd, $outputfile);
  38. last;
  39. }
  40. }
  41. }
  42. @gcc_cmd = map { /\.[csS]$/ ? qw(-x assembler -) : $_ } @gcc_cmd;
  43. @preprocess_c_cmd = map { /\.o$/ ? "-" : $_ } @preprocess_c_cmd;
  44. my $comm;
  45. # detect architecture from gcc binary name
  46. if ($gcc_cmd[0] =~ /arm/) {
  47. $comm = '@';
  48. } elsif ($gcc_cmd[0] =~ /powerpc|ppc/) {
  49. $comm = '#';
  50. }
  51. # look for -arch flag
  52. foreach my $i (1 .. $#gcc_cmd-1) {
  53. if ($gcc_cmd[$i] eq "-arch") {
  54. if ($gcc_cmd[$i+1] =~ /arm/) {
  55. $comm = '@';
  56. } elsif ($gcc_cmd[$i+1] =~ /powerpc|ppc/) {
  57. $comm = '#';
  58. }
  59. }
  60. }
  61. # assume we're not cross-compiling if no -arch or the binary doesn't have the arch name
  62. if (!$comm) {
  63. my $native_arch = qx/arch/;
  64. if ($native_arch =~ /arm/) {
  65. $comm = '@';
  66. } elsif ($native_arch =~ /powerpc|ppc/) {
  67. $comm = '#';
  68. }
  69. }
  70. if (!$comm) {
  71. die "Unable to identify target architecture";
  72. }
  73. my %ppc_spr = (ctr => 9,
  74. vrsave => 256);
  75. open(ASMFILE, "-|", @preprocess_c_cmd) || die "Error running preprocessor";
  76. my $current_macro = '';
  77. my $macro_level = 0;
  78. my %macro_lines;
  79. my %macro_args;
  80. my %macro_args_default;
  81. my $macro_count = 0;
  82. my $altmacro = 0;
  83. my @pass1_lines;
  84. my @ifstack;
  85. my %symbols;
  86. # pass 1: parse .macro
  87. # note that the handling of arguments is probably overly permissive vs. gas
  88. # but it should be the same for valid cases
  89. while (<ASMFILE>) {
  90. # remove all comments (to avoid interfering with evaluating directives)
  91. s/(?<!\\)$comm.*//x;
  92. # comment out unsupported directives
  93. s/\.type/$comm.type/x;
  94. s/\.func/$comm.func/x;
  95. s/\.endfunc/$comm.endfunc/x;
  96. s/\.ltorg/$comm.ltorg/x;
  97. s/\.size/$comm.size/x;
  98. s/\.fpu/$comm.fpu/x;
  99. s/\.arch/$comm.arch/x;
  100. s/\.object_arch/$comm.object_arch/x;
  101. # the syntax for these is a little different
  102. s/\.global/.globl/x;
  103. # also catch .section .rodata since the equivalent to .const_data is .section __DATA,__const
  104. s/(.*)\.rodata/.const_data/x;
  105. s/\.int/.long/x;
  106. s/\.float/.single/x;
  107. # catch unknown section names that aren't mach-o style (with a comma)
  108. if (/.section ([^,]*)$/) {
  109. die ".section $1 unsupported; figure out the mach-o section name and add it";
  110. }
  111. parse_line($_);
  112. }
  113. sub eval_expr {
  114. my $expr = $_[0];
  115. $expr =~ s/([A-Za-z._][A-Za-z0-9._]*)/$symbols{$1}/g;
  116. eval $expr;
  117. }
  118. sub handle_if {
  119. my $line = $_[0];
  120. # handle .if directives; apple's assembler doesn't support important non-basic ones
  121. # evaluating them is also needed to handle recursive macros
  122. if ($line =~ /\.if(n?)([a-z]*)\s+(.*)/) {
  123. my $result = $1 eq "n";
  124. my $type = $2;
  125. my $expr = $3;
  126. if ($type eq "b") {
  127. $expr =~ s/\s//g;
  128. $result ^= $expr eq "";
  129. } elsif ($type eq "c") {
  130. if ($expr =~ /(.*)\s*,\s*(.*)/) {
  131. $result ^= $1 eq $2;
  132. } else {
  133. die "argument to .ifc not recognized";
  134. }
  135. } elsif ($type eq "") {
  136. $result ^= eval_expr($expr) != 0;
  137. } elsif ($type eq "eq") {
  138. $result = eval_expr($expr) == 0;
  139. } elsif ($type eq "lt") {
  140. $result = eval_expr($expr) < 0;
  141. } else {
  142. chomp($line);
  143. die "unhandled .if varient. \"$line\"";
  144. }
  145. push (@ifstack, $result);
  146. return 1;
  147. } else {
  148. return 0;
  149. }
  150. }
  151. sub parse_line {
  152. my $line = @_[0];
  153. # evaluate .if blocks
  154. if (scalar(@ifstack)) {
  155. if (/\.endif/) {
  156. pop(@ifstack);
  157. return;
  158. } elsif ($line =~ /\.elseif\s+(.*)/) {
  159. if ($ifstack[-1] == 0) {
  160. $ifstack[-1] = !!eval_expr($1);
  161. } elsif ($ifstack[-1] > 0) {
  162. $ifstack[-1] = -$ifstack[-1];
  163. }
  164. return;
  165. } elsif (/\.else/) {
  166. $ifstack[-1] = !$ifstack[-1];
  167. return;
  168. } elsif (handle_if($line)) {
  169. return;
  170. }
  171. # discard lines in false .if blocks
  172. foreach my $i (0 .. $#ifstack) {
  173. if ($ifstack[$i] <= 0) {
  174. return;
  175. }
  176. }
  177. }
  178. if (/\.macro/) {
  179. $macro_level++;
  180. if ($macro_level > 1 && !$current_macro) {
  181. die "nested macros but we don't have master macro";
  182. }
  183. } elsif (/\.endm/) {
  184. $macro_level--;
  185. if ($macro_level < 0) {
  186. die "unmatched .endm";
  187. } elsif ($macro_level == 0) {
  188. $current_macro = '';
  189. return;
  190. }
  191. }
  192. if ($macro_level > 1) {
  193. push(@{$macro_lines{$current_macro}}, $line);
  194. } elsif ($macro_level == 0) {
  195. expand_macros($line);
  196. } else {
  197. if ($line =~ /\.macro\s+([\d\w\.]+)\s*(.*)/) {
  198. $current_macro = $1;
  199. # commas in the argument list are optional, so only use whitespace as the separator
  200. my $arglist = $2;
  201. $arglist =~ s/,/ /g;
  202. my @args = split(/\s+/, $arglist);
  203. foreach my $i (0 .. $#args) {
  204. my @argpair = split(/=/, $args[$i]);
  205. $macro_args{$current_macro}[$i] = $argpair[0];
  206. $argpair[0] =~ s/:vararg$//;
  207. $macro_args_default{$current_macro}{$argpair[0]} = $argpair[1];
  208. }
  209. # ensure %macro_lines has the macro name added as a key
  210. $macro_lines{$current_macro} = [];
  211. } elsif ($current_macro) {
  212. push(@{$macro_lines{$current_macro}}, $line);
  213. } else {
  214. die "macro level without a macro name";
  215. }
  216. }
  217. }
  218. sub expand_macros {
  219. my $line = @_[0];
  220. # handle .if directives; apple's assembler doesn't support important non-basic ones
  221. # evaluating them is also needed to handle recursive macros
  222. if (handle_if($line)) {
  223. return;
  224. }
  225. if (/\.purgem\s+([\d\w\.]+)/) {
  226. delete $macro_lines{$1};
  227. delete $macro_args{$1};
  228. delete $macro_args_default{$1};
  229. return;
  230. }
  231. if ($line =~ /\.altmacro/) {
  232. $altmacro = 1;
  233. return;
  234. }
  235. if ($line =~ /\.noaltmacro/) {
  236. $altmacro = 0;
  237. return;
  238. }
  239. $line =~ s/\%([^,]*)/eval_expr($1)/eg if $altmacro;
  240. if ($line =~ /\.set\s+(.*),\s*(.*)/) {
  241. $symbols{$1} = eval_expr($2);
  242. }
  243. if ($line =~ /(\S+:|)\s*([\w\d\.]+)\s*(.*)/ && exists $macro_lines{$2}) {
  244. push(@pass1_lines, $1);
  245. my $macro = $2;
  246. # commas are optional here too, but are syntactically important because
  247. # parameters can be blank
  248. my @arglist = split(/,/, $3);
  249. my @args;
  250. my @args_seperator;
  251. my $comma_sep_required = 0;
  252. foreach (@arglist) {
  253. # allow arithmetic/shift operators in macro arguments
  254. $_ =~ s/\s*(\+|-|\*|\/|<<|>>)\s*/$1/g;
  255. my @whitespace_split = split(/\s+/, $_);
  256. if (!@whitespace_split) {
  257. push(@args, '');
  258. push(@args_seperator, '');
  259. } else {
  260. foreach (@whitespace_split) {
  261. #print ("arglist = \"$_\"\n");
  262. if (length($_)) {
  263. push(@args, $_);
  264. my $sep = $comma_sep_required ? "," : " ";
  265. push(@args_seperator, $sep);
  266. #print ("sep = \"$sep\", arg = \"$_\"\n");
  267. $comma_sep_required = 0;
  268. }
  269. }
  270. }
  271. $comma_sep_required = 1;
  272. }
  273. my %replacements;
  274. if ($macro_args_default{$macro}){
  275. %replacements = %{$macro_args_default{$macro}};
  276. }
  277. # construct hashtable of text to replace
  278. foreach my $i (0 .. $#args) {
  279. my $argname = $macro_args{$macro}[$i];
  280. my @macro_args = @{ $macro_args{$macro} };
  281. if ($args[$i] =~ m/=/) {
  282. # arg=val references the argument name
  283. # XXX: I'm not sure what the expected behaviour if a lot of
  284. # these are mixed with unnamed args
  285. my @named_arg = split(/=/, $args[$i]);
  286. $replacements{$named_arg[0]} = $named_arg[1];
  287. } elsif ($i > $#{$macro_args{$macro}}) {
  288. # more args given than the macro has named args
  289. # XXX: is vararg allowed on arguments before the last?
  290. $argname = $macro_args{$macro}[-1];
  291. if ($argname =~ s/:vararg$//) {
  292. #print "macro = $macro, args[$i] = $args[$i], args_seperator=@args_seperator, argname = $argname, arglist[$i] = $arglist[$i], arglist = @arglist, args=@args, macro_args=@macro_args\n";
  293. #$replacements{$argname} .= ", $args[$i]";
  294. $replacements{$argname} .= "$args_seperator[$i] $args[$i]";
  295. } else {
  296. die "Too many arguments to macro $macro";
  297. }
  298. } else {
  299. $argname =~ s/:vararg$//;
  300. $replacements{$argname} = $args[$i];
  301. }
  302. }
  303. my $count = $macro_count++;
  304. # apply replacements as regex
  305. foreach (@{$macro_lines{$macro}}) {
  306. my $macro_line = $_;
  307. # do replacements by longest first, this avoids wrong replacement
  308. # when argument names are subsets of each other
  309. foreach (reverse sort {length $a <=> length $b} keys %replacements) {
  310. $macro_line =~ s/\\$_/$replacements{$_}/g;
  311. }
  312. $macro_line =~ s/\\\@/$count/g;
  313. $macro_line =~ s/\\\(\)//g; # remove \()
  314. parse_line($macro_line);
  315. }
  316. } else {
  317. push(@pass1_lines, $line);
  318. }
  319. }
  320. close(ASMFILE) or exit 1;
  321. open(ASMFILE, "|-", @gcc_cmd) or die "Error running assembler";
  322. #open(ASMFILE, ">/tmp/a.S") or die "Error running assembler";
  323. my @sections;
  324. my $num_repts;
  325. my $rept_lines;
  326. my %literal_labels; # for ldr <reg>, =<expr>
  327. my $literal_num = 0;
  328. my $in_irp = 0;
  329. my @irp_args;
  330. my $irp_param;
  331. # pass 2: parse .rept and .if variants
  332. # NOTE: since we don't implement a proper parser, using .rept with a
  333. # variable assigned from .set is not supported
  334. foreach my $line (@pass1_lines) {
  335. # handle .previous (only with regard to .section not .subsection)
  336. if ($line =~ /\.(section|text|const_data)/) {
  337. push(@sections, $line);
  338. } elsif ($line =~ /\.previous/) {
  339. if (!$sections[-2]) {
  340. die ".previous without a previous section";
  341. }
  342. $line = $sections[-2];
  343. push(@sections, $line);
  344. }
  345. # handle ldr <reg>, =<expr>
  346. if ($line =~ /(.*)\s*ldr([\w\s\d]+)\s*,\s*=(.*)/) {
  347. my $label = $literal_labels{$3};
  348. if (!$label) {
  349. $label = ".Literal_$literal_num";
  350. $literal_num++;
  351. $literal_labels{$3} = $label;
  352. }
  353. $line = "$1 ldr$2, $label\n";
  354. } elsif ($line =~ /\.ltorg/) {
  355. foreach my $literal (keys %literal_labels) {
  356. $line .= "$literal_labels{$literal}:\n .word $literal\n";
  357. }
  358. %literal_labels = ();
  359. }
  360. # @l -> lo16() @ha -> ha16()
  361. $line =~ s/,\s+([^,]+)\@l\b/, lo16($1)/g;
  362. $line =~ s/,\s+([^,]+)\@ha\b/, ha16($1)/g;
  363. # move to/from SPR
  364. if ($line =~ /(\s+)(m[ft])([a-z]+)\s+(\w+)/ and exists $ppc_spr{$3}) {
  365. if ($2 eq 'mt') {
  366. $line = "$1${2}spr $ppc_spr{$3}, $4\n";
  367. } else {
  368. $line = "$1${2}spr $4, $ppc_spr{$3}\n";
  369. }
  370. }
  371. # old gas versions store upper and lower case names on .req,
  372. # but they remove only one on .unreq
  373. if ($fix_unreq) {
  374. if ($line =~ /\.unreq\s+(.*)/) {
  375. $line = ".unreq " . lc($1) . "\n";
  376. print ASMFILE ".unreq " . uc($1) . "\n";
  377. }
  378. }
  379. if ($line =~ /\.rept\s+(.*)/) {
  380. $num_repts = $1;
  381. $rept_lines = "\n";
  382. # handle the possibility of repeating another directive on the same line
  383. # .endr on the same line is not valid, I don't know if a non-directive is
  384. if ($num_repts =~ s/(\.\w+.*)//) {
  385. $rept_lines .= "$1\n";
  386. }
  387. $num_repts = eval($num_repts);
  388. } elsif ($line =~ /\.irp\s+([\d\w\.]+)\s*(.*)/) {
  389. $in_irp = 1;
  390. $num_repts = 1;
  391. $rept_lines = "\n";
  392. $irp_param = $1;
  393. # only use whitespace as the separator
  394. my $irp_arglist = $2;
  395. $irp_arglist =~ s/,/ /g;
  396. $irp_arglist =~ s/^\s+//;
  397. @irp_args = split(/\s+/, $irp_arglist);
  398. } elsif ($line =~ /\.irpc\s+([\d\w\.]+)\s*(.*)/) {
  399. $in_irp = 1;
  400. $num_repts = 1;
  401. $rept_lines = "\n";
  402. $irp_param = $1;
  403. my $irp_arglist = $2;
  404. $irp_arglist =~ s/,/ /g;
  405. $irp_arglist =~ s/^\s+//;
  406. @irp_args = split(//, $irp_arglist);
  407. } elsif ($line =~ /\.endr/) {
  408. if ($in_irp != 0) {
  409. foreach my $i (@irp_args) {
  410. my $line = $rept_lines;
  411. $line =~ s/\\$irp_param/$i/g;
  412. $line =~ s/\\\(\)//g; # remove \()
  413. print ASMFILE $line;
  414. }
  415. } else {
  416. for (1 .. $num_repts) {
  417. print ASMFILE $rept_lines;
  418. }
  419. }
  420. $rept_lines = '';
  421. $in_irp = 0;
  422. @irp_args = '';
  423. } elsif ($rept_lines) {
  424. $rept_lines .= $line;
  425. } else {
  426. print ASMFILE $line;
  427. }
  428. }
  429. print ASMFILE ".text\n";
  430. foreach my $literal (keys %literal_labels) {
  431. print ASMFILE "$literal_labels{$literal}:\n .word $literal\n";
  432. }
  433. close(ASMFILE) or exit 1;
  434. #exit 1