-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathdefs.rs
More file actions
1298 lines (1143 loc) · 49.5 KB
/
defs.rs
File metadata and controls
1298 lines (1143 loc) · 49.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
use enum_map::{EnumMap, enum_map};
use indexmap::IndexMap;
use core::fmt;
use crate::raw;
use crate::ast;
use crate::context::{self, CompilerContext};
use crate::diagnostic::{Diagnostic, IntoDiagnostics};
use crate::error::{GatherErrorIteratorExt, ErrorReported};
use crate::pos::{Sp, Span, SourceStr};
use crate::game::{Game, LanguageKey};
use crate::ident::{Ident, ResIdent};
use crate::resolve::{RegId, Namespace, DefId, NodeId, LoopId, ConstId, AliasableId, IdMap, id_map, rib};
use crate::mapfile::Mapfile;
use crate::value::{ScalarValue, ScalarType, VarType, ExprType};
use crate::llir::{InstrAbi, IntrinsicInstrKind};
/// Bit representation of the NAN constant in the compiler.
pub const CANONICAL_NAN_BITS: u32 = 0x7FC0_0000;
/// Retains information about all definitions in the program.
///
/// This object is responsible for storing information that is immediately available at the definition
/// site of a variable or function, such as: type information, spans, function signatures, expressions
/// for consts.
///
/// It is also currently the type responsible for holding type information about registers and opcodes,
/// despite these not having [`DefId`]s.
///
/// **Note:** The methods for creating new definitions are currently on [`CompilerContext`], where they can
/// more easily record information for name resolution.
#[derive(Debug, Clone)]
pub struct Defs {
regs: IdMap<(LanguageKey, RegId), RegData>,
instrs: IdMap<(LanguageKey, raw::Opcode), InsData>,
vars: IdMap<DefId, VarData>,
funcs: IdMap<DefId, FuncData>,
enums: IdMap<Ident, EnumData>,
/// Preferred alias (i.e. the one we decompile to) for each register.
reg_aliases: IdMap<(LanguageKey, RegId), DefId>,
/// Preferred alias (i.e. the one we decompile to) for each instruction.
ins_aliases: IdMap<(LanguageKey, raw::Opcode), DefId>,
global_ribs: GlobalRibs,
/// Intrinsics from mapfiles.
intrinsic_instrs: EnumMap<LanguageKey, Vec<(raw::Opcode, Sp<IntrinsicInstrKind>)>>,
/// Maps enum const names to their enum if they are unique, or to None if they are shared by
/// multiple enums.
unique_enums: IdMap<Ident, Option<Ident>>,
/// The first name resolution pass maps all enum consts to this.
enum_const_dummy_def_id: Option<DefId>,
}
/// Ribs for the global scope, which are built incrementally as things are defined.
#[derive(Debug, Clone)]
pub struct GlobalRibs {
/// Some of the initial ribs for name resolution, containing register aliases from mapfiles.
reg_alias_ribs: EnumMap<LanguageKey, rib::Rib>,
/// Some of the initial ribs for name resolution, containing instruction aliases from mapfiles.
ins_alias_ribs: EnumMap<LanguageKey, rib::Rib>,
/// One of the initial ribs for name resolution, containing consts from enums.
enum_const_rib: rib::Rib,
/// One of the initial ribs for name resolution, containing consts like INF.
builtin_const_rib: rib::Rib,
}
pub mod auto_enum_names {
use super::*;
macro_rules! define_auto_enum_names {
($( $const_func_name:ident => ($ident_str:literal, $ty:expr), )*) => {
$( pub fn $const_func_name() -> Ident { ident!($ident_str) } )*
pub fn all() -> Vec<(Ident, ScalarType)> {
vec![ $(($const_func_name(), $ty)),* ]
}
}
}
define_auto_enum_names!{
anm_sprite => ("AnmSprite", ScalarType::Int),
olde_ecl_sub => ("EclSub", ScalarType::Int),
stack_ecl_sub => ("EclSubName", ScalarType::String),
msg_script => ("MsgScript", ScalarType::Int),
anm_script => ("AnmScript", ScalarType::Int),
color_format => ("BitmapColorFormat", ScalarType::Int),
bool => ("bool", ScalarType::Int),
}
}
#[derive(Debug, Clone)]
struct RegData {
ty: VarType,
}
#[derive(Debug, Clone)]
struct VarData {
kind: VarKind,
/// Inherent type. `None` for [`VarKind::RegisterAlias`].
ty: Option<VarType>,
}
#[derive(Debug, Clone)]
enum VarKind {
RegisterAlias {
ident: ResIdent,
language: LanguageKey,
reg: RegId,
// TODO: location where alias is defined, follow the example set by InstrAbiLoc
},
Local {
/// NOTE: For auto-generated temporaries, the span may point to their expression instead.
ident: Sp<ResIdent>,
},
BuiltinConst {
ident: ResIdent,
expr: ast::Expr,
},
Const {
ident: Sp<ResIdent>,
expr: Sp<ast::Expr>,
},
// FIXME: Strictly speaking, enum consts are not vars, so this perhaps doesn't belong here.
// However, assigning them DefIds and storing their expressions in here is very
// convenient for the const evaluator.
EnumConst {
#[allow(unused)]
enum_name: Sp<Ident>,
ident: Sp<ResIdent>,
expr: Sp<ast::Expr>,
},
/// All unqualified enum consts map to this initially during name resolution.
EnumConstDummy,
}
#[derive(Debug, Clone)]
pub enum ConstExprLoc {
Span(Span), // string in a mapfile
Builtin,
}
impl From<Span> for ConstExprLoc {
fn from(s: Span) -> Self { ConstExprLoc::Span(s) }
}
#[derive(Debug, Clone)]
struct InsData {
abi_loc: InstrAbiLoc,
abi: InstrAbi,
sig: Signature,
}
/// Diagnostic information about where an instruction ABI is defined.
#[derive(Debug, Clone)]
pub enum InstrAbiLoc {
Span(Span), // string in a mapfile
CoreMapfile {
language: LanguageKey,
opcode: raw::Opcode,
abi_str: String,
},
}
#[derive(Debug, Clone)]
struct FuncData {
kind: FuncKind,
/// `None` for [`FuncKind::InstructionAlias`].
sig: Option<Signature>,
}
#[derive(Debug, Clone)]
pub enum FuncKind {
InstructionAlias {
ident: ResIdent,
language: LanguageKey,
opcode: raw::Opcode,
// TODO: location where alias is defined
},
User {
ident: Sp<ResIdent>,
qualifier: Option<Sp<ast::FuncQualifier>>,
},
}
#[derive(Debug, Clone)]
struct EnumData {
/// Where was this declared?
source: EnumDeclSource,
/// For the sake of the type checker, every enum must have a fixed type that is known up front.
ty: ScalarType,
/// Tracks all consts defined for this enum. Later entries in the IndexMap take priority
/// during decompilation.
///
/// (Under normal circumstances, Defs would prefer not to use IndexMap, and to instead
/// track the latest name for each value (e.g. a `i32 -> Ident` map). But in the future
/// when we have standard enum syntax, the consts will be able to be arbitrary exprs which
/// require a const evaluation pass.
consts: IndexMap<Sp<Ident>, ConstId>,
}
#[derive(Debug, Clone)]
enum EnumDeclSource {
Builtin,
User(Span),
}
// =============================================================================
impl Default for Defs {
fn default() -> Self {
use rib::{Rib, RibKind};
Defs {
regs: Default::default(),
instrs: Default::default(),
vars: Default::default(),
funcs: Default::default(),
enums: auto_enum_names::all().into_iter().map(|(enum_name, ty)| {
let data = EnumData {
ty, source: EnumDeclSource::Builtin,
consts: Default::default(),
};
(enum_name, data)
}).collect(),
reg_aliases: Default::default(),
ins_aliases: Default::default(),
global_ribs: GlobalRibs {
reg_alias_ribs: enum_map![language => Rib::new(Namespace::Vars, RibKind::Mapfile { language })],
ins_alias_ribs: enum_map![language => Rib::new(Namespace::Funcs, RibKind::Mapfile { language })],
enum_const_rib: Rib::new(Namespace::Vars, RibKind::EnumConsts),
builtin_const_rib: Rib::new(Namespace::Vars, RibKind::BuiltinConsts),
},
intrinsic_instrs: Default::default(),
unique_enums: Default::default(),
enum_const_dummy_def_id: None,
}
}
}
impl Defs {
pub fn new() -> Self { Default::default() }
}
/// # Definitions
impl CompilerContext<'_> {
/// Set the inherent type of a register.
pub fn set_reg_ty(&mut self, language: LanguageKey, reg: RegId, ty: VarType) {
self.defs.regs.insert((language, reg), RegData { ty });
}
/// Add an alias for a register from a mapfile.
///
/// The alias will also become the new preferred alias for decompiling that register.
pub fn define_global_reg_alias(&mut self, language: LanguageKey, reg: RegId, ident: Sp<Ident>) -> DefId {
let res_ident = self.resolutions.attach_fresh_res(ident.value.clone());
let def_id = self.create_new_def_id(&res_ident);
self.defs.vars.insert(def_id, VarData {
ty: None,
kind: VarKind::RegisterAlias { language, reg, ident: res_ident },
});
self.defs.reg_aliases.insert((language, reg), def_id);
if let Err(old) = self.defs.global_ribs.reg_alias_ribs[language].insert(sp!(ident.clone()), def_id) {
let old_reg = self.defs.var_reg(old.def_id).unwrap().1;
if old_reg != reg {
self.emitter.emit(warning!(
"name '{}' used for multiple registers in mapfiles: {}, {}",
ident, old_reg, reg,
)).ignore();
}
}
def_id
}
/// Declare a local variable, resolving the ident to a brand new [`DefId`].
pub fn define_local(&mut self, ident: Sp<ResIdent>, ty: VarType) -> DefId {
let def_id = self.create_new_def_id(&ident);
self.defs.vars.insert(def_id, VarData {
ty: Some(ty),
kind: VarKind::Local { ident },
});
def_id
}
/// Declare an enum const without a span, creating a brand new [`ConstId`].
///
/// This alternative to [`Self::define_enum_const`] takes simpler arguments and is designed
/// for use during decompilation.
pub fn define_enum_const_fresh(&mut self, ident: Ident, value: ScalarValue, enum_name: Ident) -> ConstId {
let res_ident = self.resolutions.attach_fresh_res(ident);
self.define_enum_const(sp!(res_ident), sp!(value.into()), sp!(enum_name))
}
/// Declare an enum. This may be done multiple times.
pub fn declare_enum(&mut self, ident: Sp<Ident>, ty: ScalarType) -> Result<(), ConflictingEnumTypeError> {
use std::collections::hash_map::Entry;
let source = EnumDeclSource::User(ident.span);
match self.defs.enums.entry(ident.value.clone()) {
Entry::Vacant(e) => {
e.insert(EnumData { ty, source, consts: Default::default() });
},
Entry::Occupied(e) => {
let old = e.get();
if old.ty != ty {
return Err(ConflictingEnumTypeError {
enum_name: ident.value.clone(),
new: (ty, source),
old: (old.ty, old.source.clone()),
});
}
},
}
Ok(())
}
/// Declare an enum const, creating a brand new [`ConstId`].
///
/// Panics if the enum has not been declared.
///
/// Enums have the property that name clashes are allowed within an enum type, but only if
/// they equate to the same value. This is useful for ANM sprites appearing in decompiled code,
/// as there may be e.g. multiple `sprite10`s all with an ID of `10`.
pub fn define_enum_const(&mut self, res_ident: Sp<ResIdent>, value: Sp<ast::Expr>, enum_name: Sp<Ident>) -> ConstId {
let ident = sp!(res_ident.span => res_ident.as_raw().clone());
let def_id = self.create_new_def_id(&res_ident);
let const_id = self.consts.defer_evaluation_of(def_id);
// NOTE: We can't type-check `value` here because it may be defined in terms of other variables,
// and name resolution hasn't been performed yet. We must simply trust we have the right type.
let this_enum_data = self.defs.enums.get_mut(&enum_name.value).expect("enum not defined");
self.defs.vars.insert(def_id, VarData {
ty: Some(VarType::Typed(this_enum_data.ty)),
kind: VarKind::EnumConst {
enum_name: enum_name.clone(),
ident: res_ident,
expr: value,
},
});
if let Some(old_const_id) = this_enum_data.consts.insert(ident.clone(), const_id) {
// redefinition of this name within this enum
self.consts.defer_equality_check("enum const", old_const_id, const_id);
} else {
// first definition within this enum
match self.defs.unique_enums.entry(ident.value.clone()) {
id_map::Entry::Occupied(mut entry) => {
// mark as ambiguous, replacing whatever's already there
entry.insert(None);
},
id_map::Entry::Vacant(entry) => {
entry.insert(Some(enum_name.value.clone()));
},
}
}
let _ = self.defs.global_ribs.enum_const_rib.insert(ident.clone(), self.defs.enum_const_dummy_def_id());
const_id
}
/// Define a const like `INF`.
pub fn define_builtin_const_var(&mut self, ident: Ident, value: ScalarValue) -> ConstId {
let ident = self.resolutions.attach_fresh_res(ident);
let def_id = self.create_new_def_id(&ident);
self.defs.vars.insert(def_id, VarData {
ty: Some(VarType::Typed(value.ty())),
kind: VarKind::BuiltinConst { ident: ident.clone(), expr: value.into() },
});
self.defs.global_ribs.builtin_const_rib.insert(sp!(ident.clone()), def_id).expect("redefinition of builtin");
self.consts.defer_evaluation_of(def_id)
}
/// Declare a compile-time constant variable, resolving the ident to a brand new [`DefId`].
pub fn define_const_var(&mut self, ident: Sp<ResIdent>, ty: ScalarType, expr: Sp<ast::Expr>) -> ConstId {
let def_id = self.create_new_def_id(&ident);
self.defs.vars.insert(def_id, VarData {
ty: Some(VarType::Typed(ty)),
kind: VarKind::Const { ident: ident.clone(), expr },
});
self.consts.defer_evaluation_of(def_id)
}
/// Set the low-level ABI of an instruction.
///
/// A high-level [`Signature`] will also be generated from the ABI.
pub fn set_ins_abi(&mut self, language: LanguageKey, opcode: raw::Opcode, abi: Sp<InstrAbi>, abi_loc: InstrAbiLoc) {
// also update the high-level signature
let sig = abi.create_signature(abi.span, self);
sig.validate(self).expect("invalid signature from InstrAbi");
self.defs.instrs.insert((language, opcode), InsData { abi: abi.value, abi_loc, sig });
}
/// Add an alias for an instruction from a mapfile.
///
/// The alias will also become the new preferred alias for decompiling that instruction.
pub fn define_global_ins_alias(&mut self, language: LanguageKey, opcode: raw::Opcode, ident: Sp<Ident>) -> DefId {
let res_ident = self.resolutions.attach_fresh_res(ident.value.clone());
let def_id = self.create_new_def_id(&res_ident);
self.defs.funcs.insert(def_id, FuncData {
sig: None,
kind: FuncKind::InstructionAlias { language, opcode, ident: res_ident },
});
self.defs.ins_aliases.insert((language, opcode), def_id);
if let Err(old) = self.defs.global_ribs.ins_alias_ribs[language].insert(ident.clone(), def_id) {
let old_opcode = self.defs.func_opcode(old.def_id).unwrap().1;
if opcode as u16 != old_opcode {
self.emitter.emit(warning!(
"name '{}' used for multiple opcodes in {}: {}, {}",
ident, language.descr(), old_opcode, opcode,
)).ignore();
}
};
def_id
}
/// Add a user-defined function, resolving the ident to a brand new [`DefId`]
pub fn define_user_func(
&mut self,
ident: Sp<ResIdent>,
qualifier: Option<Sp<ast::FuncQualifier>>,
siggy: Signature,
) -> DefId {
let def_id = self.create_new_def_id(&ident);
self.defs.funcs.insert(def_id, FuncData {
sig: Some(siggy),
kind: FuncKind::User { ident, qualifier },
});
def_id
}
/// Create a [`DefId`] for the name in a declaration, and automatically resolve the input
/// ident to that ID.
fn create_new_def_id(&mut self, ident: &ResIdent) -> DefId {
self.resolutions.record_self_resolution(ident)
}
/// Get a fresh node ID for a newly constructed AST node.
///
/// If you're unable to use this because a smaller portion of [`CompilerContext`] is
/// already borrowed, you may alternatively borrow [`CompilerContext::unused_node_ids`].
pub fn next_node_id(&self) -> NodeId {
self.unused_node_ids.next()
}
/// Get a fresh loop ID for a newly constructed AST loop or switch in the AST.
pub fn next_loop_id(&self) -> LoopId {
self.unused_loop_ids.next()
}
}
#[derive(Debug, Clone)]
pub struct ConflictingEnumTypeError {
enum_name: Ident,
old: (ScalarType, EnumDeclSource),
new: (ScalarType, EnumDeclSource),
}
impl IntoDiagnostics for ConflictingEnumTypeError {
fn into_diagnostics(self) -> Vec<Diagnostic> {
match (self.old.1, self.new.1) {
(_, EnumDeclSource::Builtin) => unreachable!(),
(EnumDeclSource::Builtin, EnumDeclSource::User(new_span)) => vec![error!(
message("incorrect type for enum {}", self.enum_name),
primary(new_span, "declaration with type {}", self.new.0.keyword()),
note("the built-in type of enum {} is {}", self.enum_name, self.old.0.keyword()),
)],
(EnumDeclSource::User(old_span), EnumDeclSource::User(new_span)) => vec![error!(
message("conflicting types for enum '{}", self.enum_name),
primary(old_span, "declaration with type {}", self.old.0.keyword()),
primary(new_span, "declaration with type {}", self.new.0.keyword()),
)],
}
}
}
/// # Recovering low-level information
impl Defs {
/// Get the register mapped to this variable, if it is a register alias.
///
/// Returns `None` for variables that do not represent registers.
///
/// IMPORTANT: In some formats like ANM and old ECL, local variables are also ultimately
/// allocated to registers, but that is unrelated to this and this function will always
/// return `None` for them.
///
/// # Panics
///
/// Panics if the ID does not correspond to a variable.
pub fn var_reg(&self, def_id: DefId) -> Option<(LanguageKey, RegId)> {
match self.vars[&def_id] {
VarData { kind: VarKind::RegisterAlias { reg, language, .. }, .. } => Some((language, reg)),
_ => None,
}
}
/// If this function is an instruction alias, get its opcode.
///
/// Returns `None` for functions that do not represent instructions.
///
/// # Panics
///
/// Panics if the ID does not correspond to a function.
pub fn func_opcode(&self, def_id: DefId) -> Option<(LanguageKey, raw::Opcode)> {
match self.funcs[&def_id] {
FuncData { kind: FuncKind::InstructionAlias { opcode, language, .. }, .. } => Some((language, opcode)),
_ => None,
}
}
/// Recovers the ABI of an opcode, if it is known.
pub fn ins_abi(&self, language: LanguageKey, opcode: raw::Opcode) -> Option<(&InstrAbi, &InstrAbiLoc)> {
self.instrs.get(&(language, opcode)).map(|x| (&x.abi, &x.abi_loc))
}
}
/// # Accessing high-level information
impl Defs {
/// Get the identifier that makes something a candidate for name resolution.
///
/// # Panics
///
/// Panics if the namespace is wrong.
///
/// (FIXME: this is silly because Defs ought to know what namespace each
/// id belongs to without having to be reminded...)
pub fn name(&self, ns: Namespace, def_id: DefId) -> &ResIdent {
match ns {
Namespace::Vars => self.var_name(def_id),
Namespace::Funcs => self.func_name(def_id),
}
}
/// Get the fully-resolved name of a variable.
///
/// # Panics
///
/// Panics if the ID does not correspond to a variable.
pub fn var_name(&self, def_id: DefId) -> &ResIdent {
match self.vars[&def_id] {
VarData { kind: VarKind::RegisterAlias { ref ident, .. }, .. } => ident,
VarData { kind: VarKind::Local { ref ident, .. }, .. } => ident,
VarData { kind: VarKind::Const { ref ident, .. }, .. } => ident,
VarData { kind: VarKind::BuiltinConst { ref ident, .. }, .. } => ident,
VarData { kind: VarKind::EnumConst { ref ident, .. }, .. } => ident,
VarData { kind: VarKind::EnumConstDummy { .. }, .. } => panic!("var_name called on enum dummy"),
}
}
/// Get the fully resolved name of a function.
///
/// # Panics
///
/// Panics if the ID does not correspond to a function.
pub fn func_name(&self, def_id: DefId) -> &ResIdent {
match self.funcs[&def_id] {
FuncData { kind: FuncKind::InstructionAlias { ref ident, .. }, .. } => ident,
FuncData { kind: FuncKind::User { ref ident, .. }, .. } => ident,
}
}
/// Get the inherent type of any kind of named variable. (register aliases, locals, temporaries, consts)
///
/// # Panics
///
/// Panics if the ID does not correspond to a variable.
pub fn var_inherent_ty(&self, def_id: DefId) -> VarType {
match self.vars[&def_id] {
VarData { kind: VarKind::RegisterAlias { language, reg, .. }, .. } => {
self.reg_inherent_ty(language, reg)
},
VarData { ty, .. } => ty.expect("not alias"),
}
}
/// Get the inherent type of a register.
pub fn reg_inherent_ty(&self, language: LanguageKey, reg: RegId) -> VarType {
match self.regs.get(&(language, reg)) {
Some(&RegData { ty }) => ty,
// This is a register whose type is not in any mapfile.
// This is actually fine, and is expected for stack registers.
None => VarType::Untyped { explicit: false },
}
}
/// Get the signature of any kind of named function. (instruction aliases, inline and const functions...)
///
/// # Panics
///
/// Panics if the ID does not correspond to a function.
pub fn func_signature(&self, def_id: DefId) -> Result<&Signature, InsMissingSigError> {
match self.funcs[&def_id] {
FuncData { kind: FuncKind::InstructionAlias { language, opcode, .. }, .. } => {
self.ins_signature(language, opcode)
},
FuncData { ref sig, .. } => Ok(sig.as_ref().expect("not alias")),
}
}
/// Get the high-level signature of an instruction.
fn ins_signature(&self, language: LanguageKey, opcode: raw::Opcode) -> Result<&Signature, InsMissingSigError> {
match self.instrs.get(&(language, opcode)) {
Some(InsData { sig, .. }) => Ok(sig),
None => Err(InsMissingSigError { language, opcode }),
}
}
/// Get the span most closely associated with a variable (ident at declaration site,
/// or expr of a temporary), if there is one.
///
/// # Panics
///
/// Panics if the ID does not correspond to a variable.
pub fn var_decl_span(&self, def_id: DefId) -> Option<Span> {
match &self.vars[&def_id] {
VarData { kind: VarKind::RegisterAlias { .. }, .. } => None,
VarData { kind: VarKind::Local { ident, .. }, .. } => Some(ident.span),
VarData { kind: VarKind::Const { ident, .. }, .. } => Some(ident.span),
VarData { kind: VarKind::BuiltinConst { .. }, .. } => None,
VarData { kind: VarKind::EnumConst { ident, .. }, .. } => Some(ident.span),
VarData { kind: VarKind::EnumConstDummy { .. }, .. } => None,
}
}
/// Get the span of the ident at the declaration site for any kind of function,
/// if there is such an ident.
///
/// # Panics
///
/// Panics if the ID does not correspond to a function.
pub fn func_decl_span(&self, def_id: DefId) -> Option<Span> {
match &self.funcs[&def_id] {
FuncData { kind: FuncKind::InstructionAlias { .. }, .. } => None,
FuncData { kind: FuncKind::User { ident, .. }, .. } => Some(ident.span),
}
}
/// Look up the qualifier for a user-defined function. `None` if not a user function.
///
/// # Panics
///
/// Panics if the ID does not correspond to a function.
pub fn user_func_qualifier(&self, def_id: DefId) -> Option<Option<Sp<ast::FuncQualifier>>> {
match &self.funcs[&def_id] {
FuncData { kind: FuncKind::InstructionAlias { .. }, .. } => None,
FuncData { kind: FuncKind::User { qualifier, .. }, .. } => Some(qualifier.clone()),
}
}
/// Get the expression assigned to a const var.
///
/// # Panics
///
/// Panics if the ID does not correspond to a variable.
pub fn var_const_expr(&self, def_id: DefId) -> Option<(ConstExprLoc, &ast::Expr)> {
match &self.vars[&def_id] {
VarData { kind: VarKind::Const { expr, .. }, .. } => Some((ConstExprLoc::Span(expr.span), &expr)),
VarData { kind: VarKind::BuiltinConst { expr, .. }, .. } => Some((ConstExprLoc::Builtin, expr)),
VarData { kind: VarKind::EnumConst { expr, .. }, .. } => Some((ConstExprLoc::Span(expr.span), &expr)),
_ => None,
}
}
/// Get the type of an enum.
///
/// # Panics
///
/// Panics if the enum has not been declared.
pub fn enum_ty(&self, enum_name: &Ident) -> ScalarType {
self.enums[enum_name].ty
}
}
impl CompilerContext<'_> {
pub fn init_special_defs(&mut self) {
self.define_enum_const_dummy();
self.add_builtin_consts();
}
fn add_builtin_consts(&mut self) {
self.define_builtin_const_var(ident!("NAN"), f32::from_bits(CANONICAL_NAN_BITS).into());
self.define_builtin_const_var(ident!("INF"), f32::INFINITY.into());
self.define_builtin_const_var(ident!("PI"), core::f32::consts::PI.into());
self.define_enum_const_fresh(ident!("true"), 1.into(), ident!("bool"));
self.define_enum_const_fresh(ident!("false"), 0.into(), ident!("bool"));
}
fn define_enum_const_dummy(&mut self) {
let ident = self.resolutions.attach_fresh_res(ident!("<ambiguous_enum>"));
let def_id = self.resolutions.record_self_resolution(&ident);
self.defs.vars.insert(def_id, VarData {
ty: Some(ScalarType::Int.into()),
kind: VarKind::EnumConstDummy,
});
self.defs.enum_const_dummy_def_id = Some(def_id);
}
/// Add info from a mapfile.
///
/// Its path (if one is provided) is recorded in order to emit import directives into a decompiled script file.
pub fn extend_from_mapfile(
&mut self,
path: Option<&std::path::Path>,
mapfile: &Mapfile,
// NOTE: this is only needed to check if it's < th08 or >= th08 for timeline arg0
game: Game,
) -> Result<(), ErrorReported> {
let emitter = self.emitter;
if let Some(path) = path {
self.mapfiles.push(path.to_owned());
}
for (names, signatures, language) in vec![
(&mapfile.ins_names, &mapfile.ins_signatures, mapfile.language),
(&mapfile.timeline_ins_names, &mapfile.timeline_ins_signatures, LanguageKey::Timeline),
] {
for &(opcode, ref ident) in names {
self.define_global_ins_alias(language, opcode as u16, ident.clone());
}
signatures.iter().map(|&(opcode, ref abi_str)| {
// since there's no escape syntax in mapfile values, abi_str exactly matches the source text,
// so we can construct a SourceStr
let abi_source = SourceStr::from_span(abi_str.span, &abi_str);
let abi = sp!(abi_str.span => InstrAbi::parse(abi_source, emitter)?);
abi.validate_against_language(abi_str.span, game, language, emitter)?;
let abi_loc = match mapfile.is_core_mapfile {
false => InstrAbiLoc::Span(abi_str.span),
true => InstrAbiLoc::CoreMapfile {
language, opcode: opcode as u16, abi_str: abi_str[..].into(),
},
};
self.set_ins_abi(language, opcode as u16, abi, abi_loc);
Ok::<_, ErrorReported>(())
}).collect_with_recovery::<()>()?;
}
for &(reg, ref ident) in &mapfile.gvar_names {
self.define_global_reg_alias(mapfile.language, RegId(reg), ident.clone());
}
for &(reg, ref value) in &mapfile.gvar_types {
let ty = match &value[..] {
"%" => VarType::Typed(ScalarType::Float),
"$" => VarType::Typed(ScalarType::Int),
"?" => VarType::Untyped { explicit: true },
_ => {
emitter.emit(warning!(
message("ignoring invalid variable type '{}' for gvar {}", value, reg),
primary(value, "invalid type"),
)).ignore();
continue;
},
};
self.set_reg_ty(mapfile.language, RegId(reg), ty);
}
mapfile.ins_intrinsics.iter().map(|&(opcode, ref kind_str)| {
// since there's no escape syntax in mapfile values, abi_str exactly matches the source text,
// so we can construct a SourceStr
let kind_source = SourceStr::from_span(kind_str.span, &kind_str);
let kind = sp!(kind_str.span => IntrinsicInstrKind::parse(kind_source, emitter)?);
self.defs.add_intrinsic_instr(mapfile.language, opcode as _, kind);
Ok(())
}).collect_with_recovery::<()>()?;
mapfile.difficulty_flags.iter().map(|&(index, ref flag_str)| {
let index = sp!(flag_str.span => index); // FIXME remind me why the indices don't have spans again?
let flag_str = sp!(flag_str.span => &flag_str[..]);
self.diff_flag_defs.define_flag_from_mapfile(index, flag_str)
.map_err(|e| emitter.emit(e))
}).collect_with_recovery::<()>()?;
mapfile.enums.iter().map(|(enum_name, enum_pairs)| {
self.declare_enum(enum_name.clone(), ScalarType::Int).map_err(|e| self.emitter.emit(e))?;
for &(value, ref const_name) in enum_pairs {
let value = sp!(const_name.span => value.into()); // FIXME remind me why the indices don't have spans again?
let res_ident = sp!(const_name.span => self.resolutions.attach_fresh_res(const_name.value.clone()));
self.define_enum_const(res_ident, value, enum_name.clone());
}
Ok(())
}).collect_with_recovery::<()>()?;
Ok(())
}
pub fn mapfiles_to_ast(&self) -> Vec<crate::pos::Sp<ast::LitString>> {
self.mapfiles.iter().map(|s| {
let string = s.to_str().expect("unpaired surrogate not supported!").into();
sp!(ast::LitString { string })
}).collect()
}
}
impl Defs {
/// Get the initial ribs for name resolution.
pub fn initial_ribs(&self) -> Vec<rib::Rib> {
let mut vec = vec![];
vec.extend(self.global_ribs.ins_alias_ribs.values().cloned());
vec.extend(self.global_ribs.reg_alias_ribs.values().cloned());
vec.push(self.global_ribs.builtin_const_rib.clone());
vec.push(self.global_ribs.enum_const_rib.clone());
vec
}
pub fn enum_const_dummy_def_id(&self) -> DefId {
self.enum_const_dummy_def_id.expect("Defs not initialized properly!")
}
}
impl Defs {
/// Add an intrinsic mapping from a mapfile.
pub fn add_intrinsic_instr(
&mut self,
language: LanguageKey,
opcode: raw::Opcode,
intrinsic: Sp<IntrinsicInstrKind>,
) {
self.intrinsic_instrs[language].push((opcode, intrinsic));
}
pub fn iter_intrinsic_instrs(&self, language: LanguageKey) -> impl Iterator<Item=(raw::Opcode, Sp<IntrinsicInstrKind>)> + '_ {
self.intrinsic_instrs[language].iter().copied()
}
}
// =============================================================================
/// Lookup table for decompiling constant values to enum consts.
#[derive(Debug, Clone)]
pub struct ConstNames {
pub enums: IdMap<Ident, ScalarValueMap<Sp<Ident>>>,
}
/// A map with ScalarValue keys, which uses bitwise-identity for floats.
#[derive(Debug, Clone)]
pub struct ScalarValueMap<T> {
ints: IdMap<i32, T>,
strings: IdMap<String, T>,
floats: IdMap<u32, T>,
}
impl<T> ScalarValueMap<T> {
pub fn new() -> Self {
ScalarValueMap {
ints: Default::default(),
strings: Default::default(),
floats: Default::default(),
}
}
pub fn insert(&mut self, key: ScalarValue, value: T) -> Option<T> {
match key {
ScalarValue::Int(x) => self.ints.insert(x, value),
ScalarValue::Float(x) => self.floats.insert(x.to_bits(), value),
ScalarValue::String(x) => self.strings.insert(x, value),
}
}
pub fn get(&self, key: &ScalarValue) -> Option<&T> {
match key {
ScalarValue::Int(x) => self.ints.get(x),
ScalarValue::Float(x) => self.floats.get(&x.to_bits()),
ScalarValue::String(x) => self.strings.get(x),
}
}
}
impl CompilerContext<'_> {
/// Get `value -> name` mappings for all enums and automatic consts, for decompilation or
/// pretty-printing purposes.
///
/// Requires [`crate::passes::evaluate_const_vars`] to have been run.
pub fn get_const_names(&self, _const_proof: crate::passes::evaluate_const_vars::Proof) -> ConstNames {
let CompilerContext { defs, consts, .. } = self;
ConstNames {
enums: defs.enums.iter().map(|(key, data)| (key.clone(), data.generate_lookup(consts))).collect(),
}
}
}
impl EnumData {
fn generate_lookup(&self, consts: &context::Consts) -> ScalarValueMap<Sp<Ident>> {
let mut out = ScalarValueMap::new();
for (ident, &const_id) in &self.consts {
let value = consts.get_cached_value(const_id).expect("evaluate_const_vars has not run! (bug!)");
out.insert(value.clone(), ident.clone());
}
out
}
}
// =============================================================================
/// Error indicating that raw syntax (`ins_`, `$REG`) was used in a place not associated with any
/// particular [`InstrLanguage`]. (e.g. a `const` definition)
pub struct UnexpectedRawSyntaxError;
impl CompilerContext<'_> {
/// Get the effective type of a variable at a place where it is referenced.
///
/// This can be different from the variable's innate type. E.g. an integer global `I0` can be
/// cast as a float using `%I0`.
pub fn var_read_ty_from_ast(&self, var: &ast::Var) -> VarType {
match var.ty_sigil {
Some(sigil) => VarType::Typed(sigil.into()),
None => self.var_inherent_ty_from_ast(var),
}
}
/// Get the innate type of a variable at a place where it is referenced, ignoring its sigils.
///
/// # Panics
///
/// Panics if there is `REG` syntax and `language` is `None`; this should be caught in an earlier pass.
pub fn var_inherent_ty_from_ast(&self, var: &ast::Var) -> VarType {
match var.name {
ast::VarName::Reg { reg, language } => self.defs.reg_inherent_ty(language.expect("must run assign_languages pass!"), reg),
ast::VarName::Normal { ref ident, .. } => self.defs.var_inherent_ty(self.resolutions.expect_def(ident)),
}
}
/// If the variable is a register or register alias, gets the associated register id.
///
/// Otherwise, it must be something else (e.g. a local, a const...), whose unique
/// name resolution id is returned.
pub fn var_reg_from_ast(&self, var: &ast::VarName) -> Result<(LanguageKey, RegId), DefId> {
match var {
&ast::VarName::Reg { reg, language, .. } => {
Ok((language.expect("must run assign_languages pass!"), reg)) // register
},
ast::VarName::Normal { ident, .. } => {
let def_id = self.resolutions.expect_def(ident);
match self.defs.var_reg(def_id) {
Some(reg) => Ok(reg), // register alias
None => Err(def_id), // something else
}
},
}
}
/// If the function name is an instruction or instruction alias, gets the associated register id.
///
/// Otherwise, it must be something else (e.g. an exported or const function...), whose unique
/// name resolution id is returned.
pub fn func_opcode_from_ast(&self, name: &ast::CallableName) -> Result<(LanguageKey, u16), DefId> {
match name {
&ast::CallableName::Ins { opcode, language, .. } => {
Ok((language.expect("must run assign_languages pass!"), opcode)) // instruction
},
ast::CallableName::Normal { ident, .. } => {
let def_id = self.resolutions.expect_def(ident);
match self.defs.func_opcode(def_id) {
Some(opcode) => Ok(opcode), // instruction alias
None => Err(def_id), // something else
}
},
}
}
/// Get the signature of any kind of callable function. (instructions, inline and const functions...)
///
/// # Panics
///
/// Panics if there is `ins_` syntax and `language` is `None`; this should be caught in an earlier pass.
pub fn func_signature_from_ast(&self, name: &ast::CallableName) -> Result<&Signature, InsMissingSigError> {
match *name {
ast::CallableName::Ins { opcode, language } => self.defs.ins_signature(language.expect("must run assign_languages pass!"), opcode),
ast::CallableName::Normal { ref ident, .. } => self.defs.func_signature(self.resolutions.expect_def(ident)),
}
}
/// Suppress the type sigil of a variable if it is unnecessary.
pub fn var_simplify_ty_sigil(&self, var: &mut ast::Var) {
let inherent_ty = self.var_inherent_ty_from_ast(var);
Self::var_simplify_ty_sigil_(&mut var.ty_sigil, inherent_ty)
}