Make JetStream 2
[WebKit-https.git] / PerformanceTests / JetStream2 / RexBench / OfflineAssembler / ast.js
1 /*
2  * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 "use strict";
26
27 /*
28  * Base utility types for the AST.
29  *
30  * Valid methods for Node:
31  *
32  * node.children -> Returns an array of immediate children.  It has been modified 
33  *     from the original Ruby version to dump directly nearly the original source.
34  *
35  * node.descendents -> Returns an array of all strict descendants (children
36  *     and children of children, transitively).
37  *
38  */
39
40 class Node
41 {
42     constructor(codeOrigin)
43     {
44         this._codeOrigin = codeOrigin;
45     }
46
47     get codeOriginString()
48     {
49         return this._codeOrigin.toString();
50     }
51
52     get codeOrigin()
53     {
54         return this._codeOrigin;
55     }
56 }
57
58 class NoChildren extends Node
59 {
60     constructor(codeOrigin)
61     {
62         super(codeOrigin);
63     }
64
65     children()
66     {
67         return [];
68     }
69 }
70
71 function structOffsetKey(struct, field)
72 {
73     return struct + "::" + field;
74 }
75
76 // AST nodes.
77
78 class StructOffset extends NoChildren
79 {
80     constructor(codeOrigin, struct, field)
81     {
82         super(codeOrigin);
83         this._struct = struct;
84         this._field = field;
85     }
86
87     static forField(codeOrigin, struct, field)
88     {
89         let key = structOffsetKey(struct, field);
90
91         if (!this.mapping[key])
92             this.mapping[key] = new StructOffset(codeOrigin, struct, field);
93
94         return this.mapping[key];
95     }
96
97     static resetMappings()
98     {
99         this.mapping = {};
100     }
101
102     dump()
103     {
104         return this.struct + "::" + this.field;
105     }
106
107     compare(other)
108     {
109         if (this.struct != other.struct)
110             return this.struct.localeCompare(other.struct);
111
112         return this.field.localeCompare(other.field);
113     }
114
115     get struct()
116     {
117         return this._struct;
118     }
119
120     get field()
121     {
122         return this._field;
123     }
124
125     get isAddress()
126     {
127         return false;
128     }
129
130     get isLabel()
131     {
132         return false;
133     }
134
135     get isImmediate()
136     {
137         return true;
138     }
139
140     get isRegister()
141     {
142         return false;
143     }
144 }
145
146 StructOffset.mapping = {};
147
148 class Sizeof extends NoChildren
149 {
150     constructor(codeOrigin, struct)
151     {
152         super(codeOrigin);
153         this._struct = struct;
154     }
155
156     static forName(codeOrigin, struct)
157     {
158         if (!this.mapping[struct])
159             this.mapping[struct] = new Sizeof(codeOrigin, struct);
160
161         return this.mapping[struct];
162     }
163
164     static resetMappings()
165     {
166         this.mapping = {};
167     }
168
169     dump()
170     {
171         return "sizeof " + this.struct;
172     }
173
174     compare(other)
175     {
176         return this.struct.localeCompare(other.struct);
177     }
178
179     get struct()
180     {
181         return this._struct;
182     }
183
184     get isAddress()
185     {
186         return false;
187     }
188
189     get isLabel()
190     {
191         return false;
192     }
193
194     get isImmediate()
195     {
196         return true;
197     }
198
199     get isRegister()
200     {
201         return false;
202     }
203 }
204
205 Sizeof.mapping = {};
206
207 class Immediate extends NoChildren
208 {
209     constructor(codeOrigin, value)
210     {
211         super(codeOrigin);
212         if (value instanceof Number)
213             value = value.valueOf();
214         this._value = value;
215         if (typeof(value) != "number")
216             throw "Bad immediate value " + value.inspect() + " at " + this.codeOriginString();
217     }
218
219     dump()
220     {
221         return this.value.toString();
222     }
223
224     equals(other)
225     {
226         return other instanceof Immediate && other.value == this.value;
227     }
228
229     get value()
230     {
231         return this._value;
232     }
233
234     get isAddress()
235     {
236         return false;
237     }
238
239     get isLabel()
240     {
241         return false;
242     }
243
244     get isImmediate()
245     {
246         return true;
247     }
248
249     get isImmediateOperand()
250     {
251         return true;
252     }
253
254     get isRegister()
255     {
256         return false;
257     }
258 }
259
260 class AddImmediates extends Node
261 {
262     constructor(codeOrigin, left, right)
263     {
264         super(codeOrigin);
265         this._left = left;
266         this._right = right;
267     }
268
269     children()
270     {
271         return [this.left, this.right];
272     }
273
274     dump()
275     {
276         return "(" + this.left.dump() + " + " + this.right.dump() + ")";
277     }
278
279     value()
280     {
281         return (this.left.value() + this.right.value()).toString();
282     }
283
284     get left()
285     {
286         return this._left;
287     }
288
289     get right()
290     {
291         return this._right;
292     }
293
294     get isAddress()
295     {
296         return false;
297     }
298
299     get isLabel()
300     {
301         return false;
302     }
303
304     get isImmediate()
305     {
306         return true;
307     }
308
309     get isImmediateOperand()
310     {
311         return true;
312     }
313
314     get isRegister()
315     {
316         return false;
317     }
318 }
319
320 class SubImmediates extends Node
321 {
322     constructor(codeOrigin, left, right)
323     {
324         super(codeOrigin);
325         this._left = left;
326         this._right = right;
327     }
328
329     children()
330     {
331         return [this.left, this.right];
332     }
333
334     dump()
335     {
336         return "(" + this.left.dump() + " - " + this.right.dump() + ")";
337     }
338
339     value()
340     {
341         return (this.left.value() - this.right.value()).toString();
342     }
343
344     get left()
345     {
346         return this._left;
347     }
348
349     get right()
350     {
351         return this._right;
352     }
353
354     get isAddress()
355     {
356         return false;
357     }
358
359     get isLabel()
360     {
361         return false;
362     }
363
364     get isImmediate()
365     {
366         return true;
367     }
368
369     get isImmediateOperand()
370     {
371         return true;
372     }
373
374     get isRegister()
375     {
376         return false;
377     }
378 }
379
380 class MulImmediates extends Node
381 {
382     constructor(codeOrigin, left, right)
383     {
384         super(codeOrigin);
385         this._left = left;
386         this._right = right;
387     }
388
389     children()
390     {
391         return [this.left, this.right];
392     }
393
394     dump()
395     {
396         return "(" + this.left.dump() + " * " + this.right.dump() + ")";
397     }
398
399     get left()
400     {
401         return this._left;
402     }
403
404     get right()
405     {
406         return this._right;
407     }
408
409     get isAddress()
410     {
411         return false;
412     }
413
414     get isLabel()
415     {
416         return false;
417     }
418
419     get isImmediate()
420     {
421         return true;
422     }
423
424     get isImmediateOperand()
425     {
426         return false;
427     }
428
429     get isRegister()
430     {
431         return false;
432     }
433 }
434
435 class NegImmediate extends Node
436 {
437     constructor(codeOrigin, child)
438     {
439         super(codeOrigin);
440         this._child = child;
441     }
442
443     children()
444     {
445         return [this.child];
446     }
447
448     dump()
449     {
450         return "(-" + this.child.dump() + ")";
451     }
452
453     get child()
454     {
455         return this._child;
456     }
457
458     get isAddress()
459     {
460         return false;
461     }
462
463     get isLabel()
464     {
465         return false;
466     }
467
468     get isImmediate()
469     {
470         return true;
471     }
472
473     get isImmediateOperand()
474     {
475         return false;
476     }
477
478     get isRegister()
479     {
480         return false;
481     }
482 }
483
484 class OrImmediates extends Node
485 {
486     constructor(codeOrigin, left, right)
487     {
488         super(codeOrigin);
489         this._left = left;
490         this._right = right;
491     }
492
493     children()
494     {
495         return [this.left, this.right];
496     }
497
498     dump()
499     {
500         return "(" + this.left.dump() + " | " + this.right.dump() + ")";
501     }
502
503     get left()
504     {
505         return this._left;
506     }
507
508     get right()
509     {
510         return this._right;
511     }
512
513     get isAddress()
514     {
515         return false;
516     }
517
518     get isLabel()
519     {
520         return false;
521     }
522
523     get isImmediate()
524     {
525         return true;
526     }
527
528     get isImmediateOperand()
529     {
530         return false;
531     }
532
533     get isRegister()
534     {
535         return false;
536     }
537 }
538
539 class AndImmediates extends Node
540 {
541     constructor(codeOrigin, left, right)
542     {
543         super(codeOrigin);
544         this._left = left;
545         this._right = right;
546     }
547
548     children()
549     {
550         return [this.left, this.right];
551     }
552
553     dump()
554     {
555         return "(" + this.left.dump() + " & " + this.right.dump() + ")";
556     }
557
558     get left()
559     {
560         return this._left;
561     }
562
563     get right()
564     {
565         return this._right;
566     }
567
568     get isAddress()
569     {
570         return false;
571     }
572
573     get isLabel()
574     {
575         return false;
576     }
577
578     get isImmediate()
579     {
580         return true;
581     }
582
583     get isImmediateOperand()
584     {
585         return false;
586     }
587
588     get isRegister()
589     {
590         return false;
591     }
592 }
593
594 class XorImmediates extends Node
595 {
596     constructor(codeOrigin, left, right)
597     {
598         super(codeOrigin);
599         this._left = left;
600         this._right = right;
601     }
602
603     children()
604     {
605         return [this.left, this.right];
606     }
607
608     dump()
609     {
610         return "(" + this.left.dump() + " ^ " + this.right.dump() + ")";
611     }
612
613     get left()
614     {
615         return this._left;
616     }
617
618     get right()
619     {
620         return this._right;
621     }
622
623     get isAddress()
624     {
625         return false;
626     }
627
628     get isLabel()
629     {
630         return false;
631     }
632
633     get isImmediate()
634     {
635         return true;
636     }
637
638     get isImmediateOperand()
639     {
640         return false;
641     }
642
643     get isRegister()
644     {
645         return false;
646     }
647 }
648
649 class BitnotImmediate extends Node
650 {
651     constructor(codeOrigin, child)
652     {
653         super(codeOrigin);
654         this._child = child;
655     }
656
657     children()
658     {
659         return [this.child];
660     }
661
662     dump()
663     {
664         return "(~" + this.child.dump() + ")";
665     }
666
667     get child()
668     {
669         return this._child;
670     }
671
672     get isAddress()
673     {
674         return false;
675     }
676
677     get isLabel()
678     {
679         return false;
680     }
681
682     get isImmediate()
683     {
684         return true;
685     }
686
687     get isImmediateOperand()
688     {
689         return false;
690     }
691
692     get isRegister()
693     {
694         return false;
695     }
696 }
697
698 class StringLiteral extends NoChildren
699 {
700     constructor(codeOrigin, value)
701     {
702         super(codeOrigin);
703         if (!value instanceof String || value[0] != "\"" || value.slice(-1) != "\"")
704             throw "Bad string literal " + value.inspect() + " at " + this.codeOriginString();
705         this._value = value.slice(1, -1);
706     }
707
708     dump()
709     {
710         return "\"" + this.value + "\"";
711     }
712
713     equals(other)
714     {
715         return other instanceof StringLiteral && other.value == this.value;
716     }
717
718     get value()
719     {
720         return this._value;
721     }
722
723     get isAddress()
724     {
725         return false;
726     }
727
728     get isLabel()
729     {
730         return false;
731     }
732
733     get isImmediate()
734     {
735         return false;
736     }
737
738     get isImmediateOperand()
739     {
740         return false;
741     }
742
743     get isRegister()
744     {
745         return false;
746     }
747 }
748
749 class RegisterID extends NoChildren
750 {
751     constructor(codeOrigin, name)
752     {
753         super(codeOrigin);
754         this._name = name;
755     }
756
757     static forName(codeOrigin, name)
758     {
759         if (!this.mapping[name])
760             this.mapping[name] = new RegisterID(codeOrigin, name);
761
762         return this.mapping[name];
763     }
764
765     dump()
766     {
767         return this.name;
768     }
769
770     get name()
771     {
772         return this._name;
773     }
774
775     get isAddress()
776     {
777         return false;
778     }
779
780     get isLabel()
781     {
782         return false;
783     }
784
785     get isImmediate()
786     {
787         return false;
788     }
789
790     get isRegister()
791     {
792         return true;
793     }
794 }
795
796 RegisterID.mapping = {};
797
798 class FPRegisterID extends NoChildren
799 {
800     constructor(codeOrigin, name)
801     {
802         super(codeOrigin);
803         this._name = name;
804     }
805
806     static forName(codeOrigin, name)
807     {
808         if (!this.mapping[name])
809             this.mapping[name] = new FPRegisterID(codeOrigin, name);
810
811         return this.mapping[name];
812     }
813
814     dump()
815     {
816         return this.name;
817     }
818
819     get name()
820     {
821         return this._name;
822     }
823
824     get isAddress()
825     {
826         return false;
827     }
828
829     get isLabel()
830     {
831         return false;
832     }
833
834     get isImmediate()
835     {
836         return false;
837     }
838
839     get isImmediateOperand()
840     {
841         return false;
842     }
843
844     get isRegister()
845     {
846         return true;
847     }
848 }
849
850 FPRegisterID.mapping = {};
851
852 class SpecialRegister extends NoChildren
853 {
854     constructor(name)
855     {
856         this._name = name;
857     }
858
859     get isAddress()
860     {
861         return false;
862     }
863
864     get isLabel()
865     {
866         return false;
867     }
868
869     get isImmediate()
870     {
871         return false;
872     }
873
874     get isImmediateOperand()
875     {
876         return false;
877     }
878
879     get isRegister()
880     {
881         return true;
882     }
883 }
884
885 class Variable extends NoChildren
886 {
887     constructor(codeOrigin, name)
888     {
889         super(codeOrigin);
890         this._name = name;
891     }
892
893     static forName(codeOrigin, name)
894     {
895         if (!this.mapping[name])
896             this.mapping[name] = new Variable(codeOrigin, name);
897
898         return this.mapping[name];
899     }
900
901     dump()
902     {
903         return this.name;
904     }
905
906     get name()
907     {
908         return this._name;
909     }
910
911     inspect()
912     {
913         return "<variable " + this.name + " at " + this.codeOriginString();
914     }
915 }
916
917 Variable.mapping = {};
918
919 class Address extends Node
920 {
921     constructor(codeOrigin, base, offset)
922     {
923         super(codeOrigin);
924         this._base = base;
925         this._offset = offset;
926         if (!base instanceof Variable && !base.register)
927             throw "Bad base for address " + base.inspect() + " at " + this.codeOriginString();
928         if (!offset instanceof Variable && !offset.immediate())
929             throw "Bad offset for address " + offset.inspect() + " at " + this.codeOriginString();
930     }
931
932     withOffset(extraOffset)
933     {
934         return new Address(this.codeOrigin, this.base, new Immediate(this.codeOrigin, this.offset.value + extraOffset));
935     }
936
937     children()
938     {
939         return [this.base, this.offset];
940     }
941
942     dump()
943     {
944         return this.offset.dump() + "[" + this.base.dump() + "]";
945     }
946
947     get base()
948     {
949         return this._base;
950     }
951
952     get offset()
953     {
954         return this._offset;
955     }
956
957     get isAddress()
958     {
959         return true;
960     }
961
962     get isLabel()
963     {
964         return false;
965     }
966
967     get isImmediate()
968     {
969         return false;
970     }
971
972     get isImmediateOperand()
973     {
974         return true;
975     }
976
977     get isRegister()
978     {
979         return false;
980     }
981 }
982
983 class BaseIndex extends Node
984 {
985     constructor(codeOrigin, base, index, scale, offset)
986     {
987         super(codeOrigin);
988         this._base = base;
989         this._index = index;
990         this._scale = scale;
991         if (![1, 2, 4, 8].includes(this._scale))
992             throw "Bad scale " + this._scale + " at " + this.codeOriginString();
993         this._offset = offset;
994     }
995
996     scaleShift()
997     {
998         switch(this.scale)
999         {
1000         case 1:
1001             return 0;
1002         case 2:
1003             return 1;
1004         case 4:
1005             return 2;
1006         case 8:
1007             return 3;
1008         default:
1009             throw "Bad scale " + this.scale + " at " + this.codeOriginString();
1010         }
1011     }
1012
1013     withOffset(extraOffset)
1014     {
1015         return new BaseIndex(codeOrigin, this.base, this.index, this.scale, new Immediate(codeOrigin, this.offset.value + extraOffset));
1016     }
1017
1018     children()
1019     {
1020         return [this.base, this.index, this.offset];
1021     }
1022
1023     dump()
1024     {
1025         return this.offset.dump() + "[" + this.base.dump() + ", " + this.index.dump() + ", " + this.scale + "]";
1026     }
1027
1028     get base()
1029     {
1030         return this._base;
1031     }
1032
1033     get index()
1034     {
1035         return this._index;
1036     }
1037
1038     get scale()
1039     {
1040         return this._scale;
1041     }
1042
1043     get offset()
1044     {
1045         return this._offset;
1046     }
1047
1048     get isAddress()
1049     {
1050         return true;
1051     }
1052
1053     get isLabel()
1054     {
1055         return false;
1056     }
1057
1058     get isImmediate()
1059     {
1060         return false;
1061     }
1062
1063     get isImmediateOperand()
1064     {
1065         return false;
1066     }
1067
1068     get isRegister()
1069     {
1070         return false;
1071     }
1072 }
1073
1074 class AbsoluteAddress extends NoChildren
1075 {
1076     constructor(codeOrigin, address)
1077     {
1078         super(codeOrigin);
1079         this._address = address;
1080     }
1081
1082     withOffset(extraOffset)
1083     {
1084         return new AbsoluteAddress(codeOrigin, new Immediate(codeOrigin, this.address.value + extraOffset));
1085     }
1086
1087     dump()
1088     {
1089         return this.address.dump() + "[]";
1090     }
1091
1092     get address()
1093     {
1094         return this._address;
1095     }
1096
1097     get isAddress()
1098     {
1099         return true;
1100     }
1101
1102     get isLabel()
1103     {
1104         return false;
1105     }
1106
1107     get isImmediate()
1108     {
1109         return false;
1110     }
1111
1112     get isImmediateOperand()
1113     {
1114         return true;
1115     }
1116
1117     get isRegister()
1118     {
1119         return false;
1120     }
1121 }
1122
1123 class Instruction extends Node
1124 {
1125     constructor(codeOrigin, opcode, operands, annotation=nil)
1126     {
1127         super(codeOrigin);
1128         this._opcode = opcode;
1129         this._operands = operands;
1130         this._annotation = annotation;
1131     }
1132
1133     children()
1134     {
1135         return [];
1136     }
1137
1138     dump()
1139     {
1140         return "    " + this.opcode + " " + (this.operands.map(function(v) { return v.dump(); }).join(", "));
1141     }
1142
1143     get opcode()
1144     {
1145         return this._opcode;
1146     }
1147
1148     get operands()
1149     {
1150         return this._operands;
1151     }
1152
1153     get annotation()
1154     {
1155         return this._annotation;
1156     }
1157 }
1158
1159 class Error extends NoChildren
1160 {
1161     constructor(codeOrigin)
1162     {
1163         super(codeOrigin);
1164     }
1165
1166     dump()
1167     {
1168         return "    error";
1169     }
1170 }
1171
1172 class ConstExpr extends  NoChildren
1173 {
1174     constructor(codeOrigin, value)
1175     {
1176         super(codeOrigin);
1177         this._value = value;
1178     }
1179
1180     static forName(codeOrigin, text)
1181     {
1182         if (!this.mapping[text])
1183             this.mapping[text] = new ConstExpr(codeOrigin, text);
1184
1185         return this.mapping[text];
1186     }
1187
1188     static resetMappings()
1189     {
1190         this.mapping = {};
1191     }
1192
1193     dump()
1194     {
1195         return "constexpr (" + this.value + ")";
1196     }
1197
1198     compare(other)
1199     {
1200         return this.value(other.value);
1201     }
1202
1203     isImmediate()
1204     {
1205         return true;
1206     }
1207
1208     get variable()
1209     {
1210         return this._variable;
1211     }
1212
1213     get value()
1214     {
1215         return this._value;
1216     }
1217 }
1218
1219 ConstExpr.mapping = {};
1220
1221
1222 class ConstDecl extends Node
1223 {
1224     constructor(codeOrigin, variable, value)
1225     {
1226         super(codeOrigin);
1227         this._variable = variable;
1228         this._value = value;
1229     }
1230
1231     children()
1232     {
1233         return [this.variable, this.value];
1234     }
1235
1236     dump()
1237     {
1238         return "const " + this.variable.dump() + " = " + this.value.dump();
1239     }
1240
1241     get variable()
1242     {
1243         return this._variable;
1244     }
1245
1246     get value()
1247     {
1248         return this._value;
1249     }
1250 }
1251
1252 let _labelMapping = {};
1253 let _referencedExternLabels = [];
1254
1255 class Label extends NoChildren
1256 {
1257     constructor(codeOrigin, name)
1258     {
1259         super(codeOrigin);
1260         this._name = name;
1261         this._extern = true;
1262         this._global = false;
1263     }
1264
1265     static forName(codeOrigin, name, definedInFile)
1266     {
1267         if (_labelMapping[name]) {
1268             if (!_labelMapping[name] instanceof Label)
1269                 throw "Label name collision: " + name;
1270         } else
1271             _labelMapping[name] = new Label(codeOrigin, name);
1272
1273         if (definedInFile)
1274             _labelMapping[name].clearExtern();
1275
1276         return _labelMapping[name];
1277     }
1278
1279     static setAsGlobal(codeOrigin, name)
1280     {
1281         if (_labelMapping[name]) {
1282             let label = _labelMapping[name];
1283             if (label.isGlobal())
1284                 throw "Label: " + name + " declared global multiple times";
1285             label.setGlobal();
1286         } else {
1287             let newLabel = new Label(codeOrigin, name);
1288             newLabel.setGlobal();
1289             _labelMapping[name] = newLabel;
1290         }
1291     }
1292
1293     static resetMappings()
1294     {
1295         _labelMapping = {};
1296         _referencedExternLabels = [];
1297     }
1298
1299
1300     static resetReferenced()
1301     {
1302         _referencedExternLabels = [];
1303     }
1304
1305     clearExtern()
1306     {
1307         this._extern = false;
1308     }
1309
1310     isExtern()
1311     {
1312         return this._extern;
1313     }
1314
1315     setGlobal()
1316     {
1317         this._global = true;
1318     }
1319
1320     isGlobal()
1321     {
1322         return this._global;
1323     }
1324
1325     dump()
1326     {
1327         return this.name + ":";
1328     }
1329
1330     get name()
1331     {
1332         return this._name;
1333     }
1334 }
1335
1336 class LocalLabel extends NoChildren
1337 {
1338     constructor(codeOrigin, name)
1339     {
1340         super(codeOrigin);
1341         this._name = name;
1342     }
1343
1344     static forName(codeOrigin, name)
1345     {
1346         if (_labelMapping[name]) {
1347             if (!_labelMapping[name] instanceof LocalLabel)
1348                 throw "Label name collision: " + name;
1349         } else
1350             _labelMapping[name] = new LocalLabel(codeOrigin, name);
1351
1352         return _labelMapping[name];
1353     }
1354
1355     static unique(comment)
1356     {
1357         let newName = "_" + comment;
1358         if (_labelMapping[newName]) {
1359             while (_labelMapping[newName = "_#" + this.uniqueNameCounter + "_" + comment])
1360                 this.uniqueNameCounter++;
1361         }
1362
1363         return forName(undefined, newName);
1364     }
1365
1366     static resetMappings()
1367     {
1368         this.uniquNameCounter = 0;
1369     }
1370
1371     cleanName()
1372     {
1373         if (/^\./.test(this._name))
1374             return "_" + this._name.slice(1);
1375
1376         return this._name;
1377     }
1378
1379     isGlobal()
1380     {
1381         return false;
1382     }
1383
1384     dump()
1385     {
1386         return this.name + ":";
1387     }
1388
1389     get name()
1390     {
1391         return this._name;
1392     }
1393 }
1394
1395 LocalLabel.uniqueNameCounter = 0;
1396
1397
1398 class LabelReference extends Node
1399 {
1400     constructor(codeOrigin, label)
1401     {
1402         super(codeOrigin);
1403         this._label = label;
1404     }
1405
1406     children()
1407     {
1408         return [this.label];
1409     }
1410
1411     name()
1412     {
1413         return this.label.name;
1414     }
1415
1416     isExtern()
1417     {
1418         return _labelMapping[name] instanceof Label && _labelMapping[name].isExtern();
1419     }
1420
1421     used()
1422     {
1423         if (!_referencedExternLabels.include(this._label) && this.isExtern())
1424             _referencedExternLabels.push(this._label);
1425     }
1426
1427     dump()
1428     {
1429         return this.label.name;
1430     }
1431
1432     value()
1433     {
1434         return asmLabel();
1435     }
1436
1437     get label()
1438     {
1439         return this._label;
1440     }
1441
1442     get isAddress()
1443     {
1444         return false;
1445     }
1446
1447     get isLabel()
1448     {
1449         return true;
1450     }
1451
1452     get isImmediate()
1453     {
1454         return false;
1455     }
1456
1457     get isImmediateOperand()
1458     {
1459         return true;
1460     }
1461 }
1462
1463 class LocalLabelReference extends NoChildren
1464 {
1465     constructor(codeOrigin, label)
1466     {
1467         super(codeOrigin);
1468         this._label = label;
1469     }
1470
1471     children()
1472     {
1473         return [this._label];
1474     }
1475
1476     name()
1477     {
1478         return this.label.name;
1479     }
1480
1481     dump()
1482     {
1483         return this.label.name;
1484     }
1485
1486     value()
1487     {
1488         return asmLabel();
1489     }
1490
1491     get label()
1492     {
1493         return this._label;
1494     }
1495
1496     get isAddress()
1497     {
1498         return false;
1499     }
1500
1501     get isLabel()
1502     {
1503         return true;
1504     }
1505
1506     get isImmediate()
1507     {
1508         return false;
1509     }
1510
1511     get isImmediateOperand()
1512     {
1513         return true;
1514     }
1515 }
1516
1517 class Sequence extends Node
1518 {
1519     constructor(codeOrigin, list)
1520     {
1521         super(codeOrigin);
1522         this._list = list;
1523     }
1524
1525     children()
1526     {
1527         return this.list;
1528     }
1529
1530     dump()
1531     {
1532         return "" + this.list.map(function(v) { return v.dump()}).join("\n");
1533     }
1534
1535     get list()
1536     {
1537         return this._list;
1538     }
1539 }
1540
1541 class True extends NoChildren
1542 {
1543     constructor()
1544     {
1545         super(undefined);
1546     }
1547
1548     static instance()
1549     {
1550         return True.instance;
1551     }
1552
1553     value()
1554     {
1555         return true;
1556     }
1557
1558     dump()
1559     {
1560         return "true";
1561     }
1562 }
1563
1564 True.instance = new True();
1565
1566 class False extends NoChildren
1567 {
1568     constructor()
1569     {
1570         super(undefined);
1571     }
1572
1573     static instance()
1574     {
1575         return False.instance;
1576     }
1577
1578     value()
1579     {
1580         return false;
1581     }
1582
1583     dump()
1584     {
1585         return "false";
1586     }
1587 }
1588
1589 False.instance = new False();
1590
1591 class Setting extends NoChildren
1592 {
1593     constructor(codeOrigin, name)
1594     {
1595         super(codeOrigin);
1596         this._name = name;
1597     }
1598
1599     static forName(codeOrigin, name)
1600     {
1601         if (!this.mapping[name])
1602             this.mapping[name] = new Setting(codeOrigin, name);
1603
1604         return this.mapping[name];
1605     }
1606
1607     static resetMappings()
1608     {
1609         this.mapping = {};
1610     }
1611
1612     dump()
1613     {
1614         return this.name;
1615     }
1616
1617     get name()
1618     {
1619         return this._name;
1620     }
1621 }
1622
1623 Setting.mapping = {};
1624
1625 class And extends Node
1626 {
1627     constructor(codeOrigin, left, right)
1628     {
1629         super(codeOrigin);
1630         this._left = left;
1631         this._right = right;
1632     }
1633
1634     children()
1635     {
1636         return [this.left, this.right];
1637     }
1638
1639     dump()
1640     {
1641         return "(" + this.left.dump() + " and " + this.right.dump() + ")";
1642     }
1643
1644     get left()
1645     {
1646         return this._left;
1647     }
1648
1649     get right()
1650     {
1651         return this._right;
1652     }
1653 }
1654
1655 class Or extends Node
1656 {
1657     constructor(codeOrigin, left, right)
1658     {
1659         super(codeOrigin);
1660         this._left = left;
1661         this._right = right;
1662     }
1663
1664     children()
1665     {
1666         return [this.left, this.right];
1667     }
1668
1669     dump()
1670     {
1671         return "(" + this.left.dump() + " or " + this.right.dump() + ")";
1672     }
1673
1674     get left()
1675     {
1676         return this._left;
1677     }
1678
1679     get right()
1680     {
1681         return this._right;
1682     }
1683 }
1684
1685 class Not extends Node
1686 {
1687     constructor(codeOrigin, child)
1688     {
1689         super(codeOrigin);
1690         this._child = child;
1691     }
1692
1693     children()
1694     {
1695         return [this.child];
1696     }
1697
1698     dump()
1699     {
1700         return "(not" + this.child.dump() + ")";
1701     }
1702
1703     get child()
1704     {
1705         return this._child;
1706     }
1707 }
1708
1709 class Skip extends NoChildren
1710 {
1711     constructor(codeOrigin)
1712     {
1713         super(codeOrigin);
1714     }
1715
1716     dump()
1717     {
1718         return "    skip";
1719     }
1720 }
1721
1722 class IfThenElse extends Node
1723 {
1724     constructor(codeOrigin, predicate, thenCase)
1725     {
1726         super(codeOrigin);
1727         this._predicate = predicate;
1728         this._thenCase = thenCase;
1729         this._elseCase = new Skip(codeOrigin);
1730     }
1731
1732     children()
1733     {
1734         return [];
1735     }
1736
1737     dump()
1738     {
1739         return "if " + this.predicate.dump() + "\n" + this.thenCase.dump() + "\nelse\n" + this.elseCase.dump() + "\nend";
1740     }
1741
1742     get predicate()
1743     {
1744         return this._predicate;
1745     }
1746
1747     get thenCase()
1748     {
1749         return this._thenCase;
1750     }
1751
1752     get elseCase()
1753     {
1754         return this._elseCase;
1755     }
1756
1757     set elseCase(newElseCase)
1758     {
1759         this._elseCase = newElseCase;
1760     }
1761 }
1762
1763 class Macro extends Node
1764 {
1765     constructor(codeOrigin, name, variables, body)
1766     {
1767         super(codeOrigin);
1768         this._name = name;
1769         this._variables = variables;
1770         this._body = body;
1771     }
1772
1773     children()
1774     {
1775         return [];
1776     }
1777
1778     dump()
1779     {
1780         return "macro " + this.name + "(" + this.variables.map(function(v) { return v.dump()}).join(", ") + ")\n" + this.body.dump() + "\nend";
1781     }
1782
1783     get name()
1784     {
1785         return this._name;
1786     }
1787
1788     get variables()
1789     {
1790         return this._variables;
1791     }
1792
1793     get body()
1794     {
1795         return this._body;
1796     }
1797 }
1798
1799 class MacroCall extends Node
1800 {
1801     constructor(codeOrigin, name, operands, annotation)
1802     {
1803         super(codeOrigin);
1804         this._name = name;
1805         this._operands = operands;
1806         if (!this._operands)
1807             throw "Operands empty to Macro call " + name;
1808         this._annotation = annotation;
1809     }
1810
1811     children()
1812     {
1813         return [];
1814     }
1815
1816     dump()
1817     {
1818         return "    " + this.name + "(" + this.operands.map(function(v) { return v.dump() }).join(", ") + ")";
1819     }
1820
1821     get name()
1822     {
1823         return this._name;
1824     }
1825
1826     get operands()
1827     {
1828         return this._operands;
1829     }
1830
1831     get annotation()
1832     {
1833         return this._annotation;
1834     }
1835 }
1836
1837 function resetAST()
1838 {
1839     StructOffset.resetMappings();
1840     Sizeof.resetMappings();
1841     ConstExpr.resetMappings();
1842     Label.resetMappings();
1843     LocalLabel.resetMappings();
1844     Setting.resetMappings();
1845 }