sway_ir/
parser.rs

1//! A parser for the printed IR, useful mostly for testing.
2
3use sway_features::ExperimentalFeatures;
4use sway_types::SourceEngine;
5
6use crate::{context::Context, error::IrError};
7
8// -------------------------------------------------------------------------------------------------
9/// Parse a string produced by [`crate::printer::to_string`] into a new [`Context`].
10pub fn parse<'eng>(
11    input: &str,
12    source_engine: &'eng SourceEngine,
13    experimental: ExperimentalFeatures,
14) -> Result<Context<'eng>, IrError> {
15    let irmod = ir_builder::parser::ir_descrs(input).map_err(|err| {
16        let found = if input.len() - err.location.offset <= 20 {
17            &input[err.location.offset..]
18        } else {
19            &input[err.location.offset..][..20]
20        };
21        IrError::ParseFailure(err.to_string(), found.into())
22    })?;
23    ir_builder::build_context(irmod, source_engine, experimental)?.verify()
24}
25
26// -------------------------------------------------------------------------------------------------
27
28mod ir_builder {
29    use slotmap::KeyData;
30    use sway_features::ExperimentalFeatures;
31    use sway_types::{ident::Ident, span::Span, u256::U256, SourceEngine};
32
33    type MdIdxRef = u64;
34
35    peg::parser! {
36        pub(in crate::parser) grammar parser() for str {
37            pub(in crate::parser) rule ir_descrs() -> IrAstModule
38                = _ sop:script_or_predicate() eoi() {
39                    sop
40                }
41                / _ c:contract() eoi() {
42                    c
43                }
44
45            rule script_or_predicate() -> IrAstModule
46                = kind:module_kind() "{" _ configs:init_config()* _ global_vars:global_var()* _ fn_decls:fn_decl()* "}" _
47                  metadata:metadata_decls() {
48                    IrAstModule {
49                        kind,
50                        configs,
51                        global_vars,
52                        fn_decls,
53                        metadata
54                    }
55                }
56
57            rule module_kind() -> Kind
58                = "script" _ { Kind::Script }
59                / "predicate" _ { Kind::Predicate }
60
61            rule contract() -> IrAstModule
62                = "contract" _ "{" _
63                  configs:init_config()* _ global_vars:global_var()* _ fn_decls:fn_decl()* "}" _
64                  metadata:metadata_decls() {
65                    IrAstModule {
66                        kind: crate::module::Kind::Contract,
67                        configs,
68                        global_vars,
69                        fn_decls,
70                        metadata
71                    }
72                }
73
74            rule global_var() -> IrAstGlobalVar
75                = "global" _ m:("mut" _)? name:path() _ ":" _ ty:ast_ty() init:global_init()? {
76                    IrAstGlobalVar {
77                        name,
78                        ty,
79                        init,
80                        mutable: m.is_some(),
81                    }
82                }
83
84            rule global_init() -> IrAstOperation
85                = "=" _ cv:op_const() {
86                    cv
87                }
88
89            rule config_encoded_bytes() -> Vec<u8>
90                = "0x" s:$(hex_digit()*) _ {
91                    hex_string_to_vec(s)
92                }
93
94            rule init_config() -> IrAstConfig
95                = value_name:value_assign() "config" _ val_ty:ast_ty() _ "," _ decode_fn:id() _ "," _ encoded_bytes:config_encoded_bytes()
96                metadata:comma_metadata_idx()? {
97                    IrAstConfig {
98                        value_name,
99                        ty: val_ty,
100                        encoded_bytes,
101                        decode_fn,
102                        metadata,
103                    }
104                }
105
106            rule fn_decl() -> IrAstFnDecl
107                = is_public:is_public() _ is_entry:is_entry() _  is_original_entry:is_original_entry() _ is_fallback:is_fallback() _ "fn" _
108                        name:id() _ selector:selector_id()? _ "(" _
109                        args:(block_arg() ** comma()) ")" _ "->" _ ret_type:ast_ty()
110                            metadata:comma_metadata_idx()? "{" _
111                        locals:fn_local()*
112                        blocks:block_decl()*
113                    "}" _ {
114                    // TODO: Remove once old decoding is removed.
115                    //       In the case of old decoding, every entry is at the same time an original entry, but in the IR
116                    //       we mark them only as `entry`s so there is a bit of information lost at the roundtrip.
117                    //       Remove this hack to recognize the new encoding once it becomes the only encoding.
118                    let is_original_entry = is_original_entry || (is_entry && !name.starts_with("__entry"));
119                    IrAstFnDecl {
120                        name,
121                        args,
122                        ret_type,
123                        is_public,
124                        metadata,
125                        locals,
126                        blocks,
127                        selector,
128                        is_entry,
129                        is_original_entry,
130                        is_fallback,
131                    }
132                }
133
134            rule is_public() -> bool
135                = "pub" _ { true }
136                / "" _ { false }
137
138            rule is_entry() -> bool
139                = "entry" _ { true }
140                / "" _ { false }
141
142            rule is_original_entry() -> bool
143                = "entry_orig" _ { true }
144                / "" _ { false }
145
146            rule is_fallback() -> bool
147                = "fallback" _ { true }
148                / "" _ { false }
149
150            rule selector_id() -> [u8; 4]
151                = "<" _ s:$(['0'..='9' | 'a'..='f' | 'A'..='F']*<8>) _ ">" _ {
152                    string_to_hex::<4>(s)
153                }
154
155            rule block_arg() -> (IrAstTy, String, Option<MdIdxRef>)
156                = name:id() mdi:metadata_idx()? ":" _ ty:ast_ty() {
157                    (ty, name, mdi)
158                }
159
160            rule fn_local() -> (IrAstTy, String, Option<IrAstOperation>, bool)
161                = "local" _ m:("mut" _)? ty:ast_ty() name:id() init:fn_local_init()? {
162                    (ty, name, init, m.is_some())
163                }
164
165            rule fn_local_init() -> IrAstOperation
166                = "=" _ cv:op_const() {
167                    cv
168                }
169
170            rule block_decl() -> IrAstBlock
171                = label:id() "(" _ args:(block_arg() ** comma()) ")" _
172                    ":" _ instructions: instr_decl()* {
173                    IrAstBlock {
174                        label,
175                        args,
176                        instructions
177                    }
178                }
179
180            rule instr_decl() -> IrAstInstruction
181                = value_name:value_assign()? op:operation() metadata:comma_metadata_idx()? {
182                    IrAstInstruction {
183                        value_name,
184                        op,
185                        metadata,
186                    }
187                }
188
189            rule value_assign() -> String
190                = name:id() "=" _ {
191                    name
192                }
193
194            rule metadata_idx() -> MdIdxRef
195                = "!" idx:decimal() {
196                    idx
197                }
198
199            rule comma_metadata_idx() -> MdIdxRef
200                = "," _ mdi:metadata_idx() {
201                    mdi
202                }
203
204            rule unary_op_kind() -> UnaryOpKind
205                = "not" _ { UnaryOpKind::Not }
206
207            rule binary_op_kind() -> BinaryOpKind
208                = "add" _ { BinaryOpKind::Add }
209                / "sub" _ { BinaryOpKind::Sub }
210                / "mul" _ { BinaryOpKind::Mul }
211                / "div" _ { BinaryOpKind::Div }
212                / "and" _ { BinaryOpKind::And }
213                / "or" _ { BinaryOpKind::Or }
214                / "xor" _ { BinaryOpKind::Xor }
215                / "mod" _ { BinaryOpKind::Mod }
216                / "rsh" _ { BinaryOpKind::Rsh }
217                / "lsh" _ { BinaryOpKind::Lsh }
218
219            rule operation() -> IrAstOperation
220                = op_asm()
221                / op_wide_unary()
222                / op_wide_binary()
223                / op_wide_cmp()
224                / op_branch()
225                / op_bitcast()
226                / op_unary()
227                / op_binary()
228                / op_call()
229                / op_cast_ptr()
230                / op_cbr()
231                / op_cmp()
232                / op_const()
233                / op_contract_call()
234                / op_get_elem_ptr()
235                / op_get_local()
236                / op_get_global()
237                / op_get_config()
238                / op_gtf()
239                / op_int_to_ptr()
240                / op_load()
241                / op_log()
242                / op_mem_copy_bytes()
243                / op_mem_copy_val()
244                / op_nop()
245                / op_ptr_to_int()
246                / op_read_register()
247                / op_ret()
248                / op_revert()
249                / op_jmp_mem()
250                / op_smo()
251                / op_state_load_quad_word()
252                / op_state_load_word()
253                / op_state_store_quad_word()
254                / op_state_store_word()
255                / op_store()
256
257            rule op_asm() -> IrAstOperation
258                = "asm" _ "(" _ args:(asm_arg() ** comma()) ")" _ ret:asm_ret() meta_idx:comma_metadata_idx()? "{" _
259                    ops:asm_op()*
260                "}" _ {
261                    IrAstOperation::Asm(
262                        args,
263                        ret.0,
264                        ret.1,
265                        ops,
266                        meta_idx
267                    )
268                }
269
270            rule op_bitcast() -> IrAstOperation
271                = "bitcast" _ val:id() "to" _ ty:ast_ty() {
272                    IrAstOperation::BitCast(val, ty)
273                }
274
275            rule op_unary() -> IrAstOperation
276                = op: unary_op_kind() arg1:id() {
277                    IrAstOperation::UnaryOp(op, arg1)
278                }
279
280            rule op_wide_modular_operation() -> IrAstOperation
281                = "wide" _ op:binary_op_kind() arg1:id() comma() arg2:id() comma() arg3:id() "to" _ result:id()  {
282                    IrAstOperation::WideModularOp(op, arg1, arg2, arg3, result)
283                }
284
285            rule op_wide_unary() -> IrAstOperation
286                = "wide" _ op:unary_op_kind() arg:id() "to" _ result:id()  {
287                    IrAstOperation::WideUnaryOp(op, arg, result)
288                }
289
290            rule op_wide_binary() -> IrAstOperation
291                = "wide" _ op:binary_op_kind() arg1:id() comma() arg2:id() "to" _ result:id()  {
292                    IrAstOperation::WideBinaryOp(op, arg1, arg2, result)
293                }
294
295            rule op_wide_cmp() -> IrAstOperation
296                = "wide" _ "cmp" _ op:cmp_pred() arg1:id() arg2:id() {
297                    IrAstOperation::WideCmp(op, arg1, arg2)
298                }
299
300            rule op_binary() -> IrAstOperation
301                = op: binary_op_kind() arg1:id() comma() arg2:id() {
302                    IrAstOperation::BinaryOp(op, arg1, arg2)
303                }
304
305            rule op_branch() -> IrAstOperation
306                = "br" _ to_block:id() "(" _ args:(id() ** comma()) ")" _ {
307                    IrAstOperation::Br(to_block, args)
308                }
309
310            rule op_call() -> IrAstOperation
311                = "call" _ callee:id() "(" _ args:(id() ** comma()) ")" _ {
312                    IrAstOperation::Call(callee, args)
313                }
314
315            rule op_cast_ptr() -> IrAstOperation
316                = "cast_ptr" _ val:id() "to" _ ty:ast_ty() {
317                    IrAstOperation::CastPtr(val, ty)
318                }
319
320            rule op_cbr() -> IrAstOperation
321                = "cbr" _ cond:id() comma() tblock:id()
322                "(" _ targs:(id() ** comma()) ")" _
323                 comma() fblock:id() "(" _ fargs:(id() ** comma()) ")" _ {
324                    IrAstOperation::Cbr(cond, tblock, targs, fblock, fargs)
325                }
326
327            rule op_cmp() -> IrAstOperation
328                = "cmp" _ p:cmp_pred() l:id() r:id() {
329                    IrAstOperation::Cmp(p, l, r)
330                }
331
332            rule op_const() -> IrAstOperation
333                = "const" _ val_ty:ast_ty() cv:constant() {
334                    IrAstOperation::Const(val_ty, cv)
335                }
336
337            rule op_contract_call() -> IrAstOperation
338                = "contract_call" _
339                ty:ast_ty() _ name:id() _
340                params:id() comma() coins:id() comma() asset_id:id() comma() gas:id() _ {
341                    IrAstOperation::ContractCall(ty, name, params, coins, asset_id, gas)
342            }
343
344            rule op_get_elem_ptr() -> IrAstOperation
345                = "get_elem_ptr" _ base:id() comma() ty:ast_ty() comma() idcs:(id() ++ comma()) {
346                    IrAstOperation::GetElemPtr(base, ty, idcs)
347            }
348
349            rule op_get_local() -> IrAstOperation
350                = "get_local" _ ast_ty() comma() name:id() {
351                    IrAstOperation::GetLocal(name)
352                }
353            rule op_get_global() -> IrAstOperation
354                = "get_global" _ ast_ty() comma() name:path() {
355                    IrAstOperation::GetGlobal(name)
356                }
357            rule op_get_config() -> IrAstOperation
358                = "get_config" _ ast_ty() comma() name:id() {
359                    IrAstOperation::GetConfig(name)
360                }
361
362            rule op_gtf() -> IrAstOperation
363                = "gtf" _ index:id() comma() tx_field_id:decimal()  {
364                    IrAstOperation::Gtf(index, tx_field_id)
365                }
366
367            rule op_int_to_ptr() -> IrAstOperation
368                = "int_to_ptr" _ val:id() "to" _ ty:ast_ty() {
369                    IrAstOperation::IntToPtr(val, ty)
370                }
371
372            rule op_load() -> IrAstOperation
373                = "load" _ src:id() {
374                    IrAstOperation::Load(src)
375                }
376
377            rule op_log() -> IrAstOperation
378                = "log" _ log_ty:ast_ty() log_val:id() comma() log_id:id() {
379                    IrAstOperation::Log(log_ty, log_val, log_id)
380                }
381
382            rule op_mem_copy_bytes() -> IrAstOperation
383                = "mem_copy_bytes" _ dst_name:id() comma() src_name:id() comma() len:decimal() {
384                    IrAstOperation::MemCopyBytes(dst_name, src_name, len)
385                }
386
387            rule op_mem_copy_val() -> IrAstOperation
388                = "mem_copy_val" _ dst_name:id() comma() src_name:id() {
389                    IrAstOperation::MemCopyVal(dst_name, src_name)
390                }
391
392            rule op_nop() -> IrAstOperation
393                = "nop" _ {
394                    IrAstOperation::Nop
395                }
396
397            rule op_ptr_to_int() -> IrAstOperation
398                = "ptr_to_int" _ val:id() "to" _ ty:ast_ty() {
399                    IrAstOperation::PtrToInt(val, ty)
400                }
401
402            rule op_read_register() -> IrAstOperation
403                = "read_register" _ r:reg_name() {
404                    IrAstOperation::ReadRegister(r)
405                }
406
407            rule op_ret() -> IrAstOperation
408                = "ret" _ ty:ast_ty() vn:id() {
409                    IrAstOperation::Ret(ty, vn)
410                }
411
412            rule op_revert() -> IrAstOperation
413                = "revert" _ vn:id() {
414                    IrAstOperation::Revert(vn)
415                }
416
417            rule op_jmp_mem() -> IrAstOperation
418                = "jmp_mem" _ {
419                    IrAstOperation::JmpMem
420                }
421
422            rule op_smo() -> IrAstOperation
423                = "smo" _
424                recipient_and_message:id() comma() message_size:id() comma() output_index:id() comma() coins:id() _ {
425                    IrAstOperation::Smo(recipient_and_message, message_size, output_index, coins)
426            }
427
428            rule op_state_clear() -> IrAstOperation
429                = "state_clear" _ "key" _ key:id() comma()  number_of_slots:id() {
430                    IrAstOperation::StateClear(key, number_of_slots)
431                }
432
433            rule op_state_load_quad_word() -> IrAstOperation
434                = "state_load_quad_word" _ dst:id() comma() "key" _ key:id() comma()  number_of_slots:id() {
435                    IrAstOperation::StateLoadQuadWord(dst, key, number_of_slots)
436                }
437
438            rule op_state_load_word() -> IrAstOperation
439                = "state_load_word" _ "key" _ key:id() {
440                    IrAstOperation::StateLoadWord(key)
441                }
442
443            rule op_state_store_quad_word() -> IrAstOperation
444                = "state_store_quad_word" _ src:id() comma() "key" _ key:id() comma()  number_of_slots:id() {
445                    IrAstOperation::StateStoreQuadWord(src, key, number_of_slots)
446                }
447
448            rule op_state_store_word() -> IrAstOperation
449                = "state_store_word" _ src:id() comma() "key" _ key:id() {
450                    IrAstOperation::StateStoreWord(src, key)
451                }
452
453            rule op_store() -> IrAstOperation
454                = "store" _ val:id() "to" _ dst:id() {
455                    IrAstOperation::Store(val, dst)
456                }
457
458            rule cmp_pred() -> Predicate
459                = "eq" _ { Predicate::Equal }
460                / "gt" _ { Predicate::GreaterThan }
461                / "lt" _ { Predicate::LessThan }
462
463            rule reg_name() -> String
464                = r:$("of" / "pc" / "ssp" / "sp" / "fp" / "hp" / "err" / "ggas" / "cgas" / "bal" / "is" / "ret" / "retl" / "flag") _ {
465                    r.to_string()
466                }
467
468            rule asm_arg() -> (Ident, Option<IrAstAsmArgInit>)
469                = name:id_id() init:asm_arg_init()? {
470                    (name, init)
471            }
472
473            rule asm_arg_init() -> IrAstAsmArgInit
474                = ":" _ imm:constant() {
475                    IrAstAsmArgInit::Imm(imm)
476                }
477                / ":" _ var:id() {
478                    IrAstAsmArgInit::Var(var)
479                }
480
481            rule asm_ret() -> (IrAstTy, Option<Ident>)
482                = "->" _ ty:ast_ty() ret:id_id()? {
483                    (ty, ret)
484                }
485
486            rule asm_op() -> IrAstAsmOp
487                = name:id_id() args:asm_op_arg()* imm:asm_op_arg_imm()? meta_idx:comma_metadata_idx()? {
488                    IrAstAsmOp {
489                        name,
490                        args,
491                        imm,
492                        meta_idx
493                    }
494                }
495
496            rule asm_op_arg() -> Ident
497                = !asm_op_arg_imm() arg:id_id() {
498                    arg
499                }
500
501            rule asm_op_arg_imm() -> Ident
502                = imm:$("i" d:decimal()) {
503                    Ident::new(Span::new(imm.into(), 0, imm.len(), None).unwrap())
504                }
505
506            rule constant() -> IrAstConst
507                = value:constant_value() meta_idx:metadata_idx()? {
508                    IrAstConst {
509                        value,
510                        meta_idx
511                    }
512                }
513
514            rule constant_value() -> IrAstConstValue
515                = "()" _ { IrAstConstValue::Unit }
516                / "true" _ { IrAstConstValue::Bool(true) }
517                / "false" _ { IrAstConstValue::Bool(false) }
518                / "0x" s:$(hex_digit()*<64>) _ {
519                    IrAstConstValue::Hex256(string_to_hex::<32>(s))
520                }
521                / n:decimal() { IrAstConstValue::Number(n) }
522                / string_const()
523                / array_const()
524                / struct_const()
525
526            rule string_const() -> IrAstConstValue
527                = ['"'] chs:str_char()* ['"'] _ {
528                    IrAstConstValue::String(chs)
529                }
530
531            rule str_char() -> u8
532                // Match any of the printable characters except '"' and '\'.
533                = c:$([' ' | '!' | '#'..='[' | ']'..='~']) {
534                    *c.as_bytes().first().unwrap()
535                }
536                / "\\x" h:hex_digit() l:hex_digit() {
537                    (h << 4) | l
538                }
539
540            //  There may be a better way to do this, dunno.  In `str_char()` we're parsing '\xHH'
541            //  from a hex byte to a u8.  We do it by parsing each hex nybble into a u8 and then OR
542            //  them together.  In hex_digit(), to convert e.g., 'c' to 12, we match the pattern,
543            //  convert the str into a u8 iterator, take the first value which is the ascii digit,
544            //  convert the 'A'-'F' to uppercase by setting the 6th bit (0x20) and subtracting the
545            //  right offset.  Fiddly.
546            rule hex_digit() -> u8
547                = d:$(['0'..='9']) {
548                    d.as_bytes().first().unwrap() - b'0'
549                }
550                / d:$(['a'..='f' | 'A'..='F']) {
551                    (d.as_bytes().first().unwrap() | 0x20) - b'a' + 10
552                }
553
554            rule array_const() -> IrAstConstValue
555                = "[" _ els:(field_or_element_const() ++ comma()) "]" _ {
556                    let el_ty = els[0].0.clone();
557                    let els = els.into_iter().map(|(_, cv)| cv).collect::<Vec<_>>();
558                    IrAstConstValue::Array(el_ty, els)
559                }
560
561            rule struct_const() -> IrAstConstValue
562                = "{" _ flds:(field_or_element_const() ** comma()) "}" _ {
563                    IrAstConstValue::Struct(flds)
564                }
565
566            rule field_or_element_const() -> (IrAstTy, IrAstConst)
567                = ty:ast_ty() cv:constant() {
568                    (ty, cv)
569                }
570                / ty:ast_ty() "undef" _ {
571                    (ty.clone(), IrAstConst { value: IrAstConstValue::Undef, meta_idx: None })
572                }
573
574            rule ast_ty() -> IrAstTy
575                = ("unit" / "()") _ { IrAstTy::Unit }
576                / "bool" _ { IrAstTy::Bool }
577                / "u8" _ { IrAstTy::U8 }
578                / "u64" _ { IrAstTy::U64 }
579                / "u256" _ { IrAstTy::U256 }
580                / "b256" _ { IrAstTy::B256 }
581                / "slice" _ { IrAstTy::Slice }
582                / "string" _ "<" _ sz:decimal() ">" _ { IrAstTy::String(sz) }
583                / array_ty()
584                / struct_ty()
585                / union_ty()
586                / "ptr" _ ty:ast_ty() { IrAstTy::Ptr(Box::new(ty)) }
587
588            rule array_ty() -> IrAstTy
589                = "[" _ ty:ast_ty() ";" _ c:decimal() "]" _ {
590                    IrAstTy::Array(Box::new(ty), c)
591                }
592
593            rule union_ty() -> IrAstTy
594                = "(" _ tys:(ast_ty() ++ ("|" _)) ")" _ {
595                    IrAstTy::Union(tys)
596                }
597
598            rule struct_ty() -> IrAstTy
599                = "{" _ tys:(ast_ty() ** comma()) "}" _ {
600                    IrAstTy::Struct(tys)
601                }
602
603            rule id() -> String
604                = !ast_ty() id:$(id_char0() id_char()*) _ {
605                    id.to_owned()
606                }
607
608            rule id_id() -> Ident
609                = !ast_ty() id:$(id_char0() id_char()*) _ {
610                    Ident::new(Span::new(id.into(), 0, id.len(), None).unwrap())
611                }
612
613            rule path() -> Vec<String>
614                = (id() ** "::")
615
616            // Metadata decls are sensitive to the newlines since the assignee idx could belong to
617            // the previous decl otherwise.  e.g.,
618            //
619            //   !1 = blah !2
620            //   !2 = 42
621            //
622            // If we did not make newlines significant we could parse the first struct as
623            // `!1 = blah !2 !2` and then get an error on the following `=`.
624            //
625            // An alternative is to put some other delimiter around naked indices, but using
626            // newlines below hasn't been that painful, so that'll do for now.
627
628            rule metadata_decls() -> Vec<(MdIdxRef, IrMetadatum)>
629                = ds:(metadata_decl() ** nl()) _ {
630                    ds
631                }
632
633            rule metadata_decl() -> (MdIdxRef, IrMetadatum)
634                = idx:metadata_idx() "=" _ item:metadata_item() {
635                    (idx, item)
636                }
637
638            // This rule (uniquely) does NOT discard the newline whitespace. `__` matches only
639            // spaces.
640            rule metadata_item() -> IrMetadatum
641                = i:dec_digits() __ {
642                    IrMetadatum::Integer(i)
643                }
644                / "!" idx:dec_digits() __ {
645                    IrMetadatum::Index(idx)
646                }
647                / ['"'] s:$(([^ '"' | '\\'] / ['\\'] ['\\' | '"' ])+) ['"'] __ {
648                    // Metadata strings are printed with '\\' escaped on parsing we unescape it.
649                    IrMetadatum::String(s.to_owned().replace("\\\\", "\\"))
650                }
651                / tag:$(id_char0() id_char()*) __ els:metadata_item()* {
652                    IrMetadatum::Struct(tag.to_owned(), els)
653                }
654                / "(" _ els:metadata_idx()*<2,> ")" __ {
655                    // Lists must contain at least 2 items, otherwise they needn't be lists.
656                    IrMetadatum::List(els)
657                }
658
659            rule id_char0()
660                = quiet!{ ['A'..='Z' | 'a'..='z' | '_'] }
661
662            rule id_char()
663                = quiet!{ id_char0() / ['0'..='9'] }
664
665            rule decimal() -> u64
666                = d:dec_digits() _ {
667                    d
668                }
669
670            // String of decimal digits without discarding whitespace. (Useful for newline
671            // sensitive metadata).
672            rule dec_digits() -> u64
673                = ds:$("0" / ['1'..='9'] ['0'..='9']*) {
674                    ds.parse::<u64>().unwrap()
675                }
676
677            rule comma()
678                = quiet!{ "," _ }
679
680            rule _()
681                = quiet!{ (space() / nl() / comment())* }
682
683            rule __()
684                = quiet!{ (space() / comment())* }
685
686            rule space()
687                = [' ' | '\t']
688
689            rule nl()
690                = ['\n' | '\r']
691
692            rule comment()
693                = "//" (!nl() [_])* nl()
694
695            rule eoi()
696                = ![_] / expected!("end of input")
697        }
698    }
699
700    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
701
702    use crate::{
703        asm::{AsmArg, AsmInstruction},
704        block::Block,
705        constant::{ConstantContent, ConstantValue},
706        context::Context,
707        error::IrError,
708        function::Function,
709        instruction::{InstOp, Predicate, Register},
710        irtype::Type,
711        metadata::{MetadataIndex, Metadatum},
712        module::{Kind, Module},
713        value::Value,
714        variable::LocalVar,
715        BinaryOpKind, BlockArgument, ConfigContent, Constant, GlobalVar, Instruction, UnaryOpKind,
716        B256,
717    };
718
719    #[derive(Debug)]
720    pub(super) struct IrAstModule {
721        kind: Kind,
722        configs: Vec<IrAstConfig>,
723        global_vars: Vec<IrAstGlobalVar>,
724        fn_decls: Vec<IrAstFnDecl>,
725        metadata: Vec<(MdIdxRef, IrMetadatum)>,
726    }
727
728    #[derive(Debug)]
729    pub(super) struct IrAstGlobalVar {
730        name: Vec<String>,
731        ty: IrAstTy,
732        init: Option<IrAstOperation>,
733        mutable: bool,
734    }
735
736    #[derive(Debug)]
737    struct IrAstFnDecl {
738        name: String,
739        args: Vec<(IrAstTy, String, Option<MdIdxRef>)>,
740        ret_type: IrAstTy,
741        is_public: bool,
742        metadata: Option<MdIdxRef>,
743        locals: Vec<(IrAstTy, String, Option<IrAstOperation>, bool)>,
744        blocks: Vec<IrAstBlock>,
745        selector: Option<[u8; 4]>,
746        is_entry: bool,
747        is_original_entry: bool,
748        is_fallback: bool,
749    }
750
751    #[derive(Debug)]
752    struct IrAstBlock {
753        label: String,
754        args: Vec<(IrAstTy, String, Option<MdIdxRef>)>,
755        instructions: Vec<IrAstInstruction>,
756    }
757
758    #[derive(Debug)]
759    struct IrAstInstruction {
760        value_name: Option<String>,
761        op: IrAstOperation,
762        metadata: Option<MdIdxRef>,
763    }
764
765    #[derive(Debug)]
766    enum IrAstOperation {
767        Asm(
768            Vec<(Ident, Option<IrAstAsmArgInit>)>,
769            IrAstTy,
770            Option<Ident>,
771            Vec<IrAstAsmOp>,
772            Option<MdIdxRef>,
773        ),
774        BitCast(String, IrAstTy),
775        UnaryOp(UnaryOpKind, String),
776        BinaryOp(BinaryOpKind, String, String),
777        Br(String, Vec<String>),
778        Call(String, Vec<String>),
779        CastPtr(String, IrAstTy),
780        Cbr(String, String, Vec<String>, String, Vec<String>),
781        Cmp(Predicate, String, String),
782        Const(IrAstTy, IrAstConst),
783        ContractCall(IrAstTy, String, String, String, String, String),
784        GetElemPtr(String, IrAstTy, Vec<String>),
785        GetLocal(String),
786        GetGlobal(Vec<String>),
787        GetConfig(String),
788        Gtf(String, u64),
789        IntToPtr(String, IrAstTy),
790        Load(String),
791        Log(IrAstTy, String, String),
792        MemCopyBytes(String, String, u64),
793        MemCopyVal(String, String),
794        Nop,
795        PtrToInt(String, IrAstTy),
796        ReadRegister(String),
797        Ret(IrAstTy, String),
798        Revert(String),
799        JmpMem,
800        Smo(String, String, String, String),
801        StateClear(String, String),
802        StateLoadQuadWord(String, String, String),
803        StateLoadWord(String),
804        StateStoreQuadWord(String, String, String),
805        StateStoreWord(String, String),
806        Store(String, String),
807        WideUnaryOp(UnaryOpKind, String, String),
808        WideBinaryOp(BinaryOpKind, String, String, String),
809        WideCmp(Predicate, String, String),
810        WideModularOp(BinaryOpKind, String, String, String, String),
811    }
812
813    #[derive(Debug)]
814    struct IrAstConfig {
815        value_name: String,
816        ty: IrAstTy,
817        encoded_bytes: Vec<u8>,
818        decode_fn: String,
819        metadata: Option<MdIdxRef>,
820    }
821
822    #[derive(Debug)]
823    struct IrAstConst {
824        value: IrAstConstValue,
825        meta_idx: Option<MdIdxRef>,
826    }
827
828    #[derive(Debug)]
829    enum IrAstConstValue {
830        Undef,
831        Unit,
832        Bool(bool),
833        Hex256([u8; 32]),
834        Number(u64),
835        String(Vec<u8>),
836        Array(IrAstTy, Vec<IrAstConst>),
837        Struct(Vec<(IrAstTy, IrAstConst)>),
838    }
839
840    #[derive(Debug)]
841    enum IrAstAsmArgInit {
842        Var(String),
843        Imm(IrAstConst),
844    }
845
846    #[derive(Debug)]
847    struct IrAstAsmOp {
848        name: Ident,
849        args: Vec<Ident>,
850        imm: Option<Ident>,
851        meta_idx: Option<MdIdxRef>,
852    }
853
854    impl IrAstConstValue {
855        fn as_constant_value(&self, context: &mut Context, val_ty: IrAstTy) -> ConstantValue {
856            match self {
857                IrAstConstValue::Undef => ConstantValue::Undef,
858                IrAstConstValue::Unit => ConstantValue::Unit,
859                IrAstConstValue::Bool(b) => ConstantValue::Bool(*b),
860                IrAstConstValue::Hex256(bs) => match val_ty {
861                    IrAstTy::U256 => {
862                        let value = U256::from_be_bytes(bs);
863                        ConstantValue::U256(value)
864                    }
865                    IrAstTy::B256 => {
866                        let value = B256::from_be_bytes(bs);
867                        ConstantValue::B256(value)
868                    }
869                    _ => unreachable!("invalid type for hex number"),
870                },
871                IrAstConstValue::Number(n) => ConstantValue::Uint(*n),
872                IrAstConstValue::String(bs) => ConstantValue::String(bs.clone()),
873                IrAstConstValue::Array(el_ty, els) => {
874                    let els: Vec<_> = els
875                        .iter()
876                        .map(|cv| {
877                            cv.value
878                                .as_constant(context, el_ty.clone())
879                                .get_content(context)
880                                .clone()
881                        })
882                        .collect();
883                    ConstantValue::Array(els)
884                }
885                IrAstConstValue::Struct(flds) => {
886                    let fields: Vec<_> = flds
887                        .iter()
888                        .map(|(ty, cv)| {
889                            cv.value
890                                .as_constant(context, ty.clone())
891                                .get_content(context)
892                                .clone()
893                        })
894                        .collect::<Vec<_>>();
895                    ConstantValue::Struct(fields)
896                }
897            }
898        }
899
900        fn as_constant(&self, context: &mut Context, val_ty: IrAstTy) -> Constant {
901            let value = self.as_constant_value(context, val_ty.clone());
902            let constant = ConstantContent {
903                ty: val_ty.to_ir_type(context),
904                value,
905            };
906            Constant::unique(context, constant)
907        }
908
909        fn as_value(&self, context: &mut Context, val_ty: IrAstTy) -> Value {
910            match self {
911                IrAstConstValue::Undef => unreachable!("Can't convert 'undef' to a value."),
912                IrAstConstValue::Unit => ConstantContent::get_unit(context),
913                IrAstConstValue::Bool(b) => ConstantContent::get_bool(context, *b),
914                IrAstConstValue::Hex256(bs) => match val_ty {
915                    IrAstTy::U256 => {
916                        let n = U256::from_be_bytes(bs);
917                        ConstantContent::get_uint256(context, n)
918                    }
919                    IrAstTy::B256 => ConstantContent::get_b256(context, *bs),
920                    _ => unreachable!("invalid type for hex number"),
921                },
922                IrAstConstValue::Number(n) => match val_ty {
923                    IrAstTy::U8 => ConstantContent::get_uint(context, 8, *n),
924                    IrAstTy::U64 => ConstantContent::get_uint(context, 64, *n),
925                    _ => unreachable!(),
926                },
927                IrAstConstValue::String(s) => ConstantContent::get_string(context, s.clone()),
928                IrAstConstValue::Array(..) => {
929                    let array_const = self.as_constant(context, val_ty);
930                    ConstantContent::get_array(context, array_const.get_content(context).clone())
931                }
932                IrAstConstValue::Struct(_) => {
933                    let struct_const = self.as_constant(context, val_ty);
934                    ConstantContent::get_struct(context, struct_const.get_content(context).clone())
935                }
936            }
937        }
938    }
939
940    #[derive(Clone, Debug)]
941    enum IrAstTy {
942        Unit,
943        Bool,
944        U8,
945        U64,
946        U256,
947        B256,
948        Slice,
949        String(u64),
950        Array(Box<IrAstTy>, u64),
951        Union(Vec<IrAstTy>),
952        Struct(Vec<IrAstTy>),
953        Ptr(Box<IrAstTy>),
954    }
955
956    impl IrAstTy {
957        fn to_ir_type(&self, context: &mut Context) -> Type {
958            match self {
959                IrAstTy::Unit => Type::get_unit(context),
960                IrAstTy::Bool => Type::get_bool(context),
961                IrAstTy::U8 => Type::get_uint8(context),
962                IrAstTy::U64 => Type::get_uint64(context),
963                IrAstTy::U256 => Type::get_uint256(context),
964                IrAstTy::B256 => Type::get_b256(context),
965                IrAstTy::Slice => Type::get_slice(context),
966                IrAstTy::String(n) => Type::new_string_array(context, *n),
967                IrAstTy::Array(el_ty, count) => {
968                    let el_ty = el_ty.to_ir_type(context);
969                    Type::new_array(context, el_ty, *count)
970                }
971                IrAstTy::Union(tys) => {
972                    let tys = tys.iter().map(|ty| ty.to_ir_type(context)).collect();
973                    Type::new_union(context, tys)
974                }
975                IrAstTy::Struct(tys) => {
976                    let tys = tys.iter().map(|ty| ty.to_ir_type(context)).collect();
977                    Type::new_struct(context, tys)
978                }
979                IrAstTy::Ptr(ty) => {
980                    let inner_ty = ty.to_ir_type(context);
981                    Type::new_ptr(context, inner_ty)
982                }
983            }
984        }
985    }
986
987    #[derive(Debug)]
988    enum IrMetadatum {
989        /// A number.
990        Integer(u64),
991        /// A reference to another metadatum.
992        Index(MdIdxRef),
993        /// An arbitrary string (e.g., a path).
994        String(String),
995        /// A tagged collection of metadata (e.g., `span !1 10 20`).
996        Struct(String, Vec<IrMetadatum>),
997        /// A collection of indices to other metadata, for attaching multiple metadata to values.
998        List(Vec<MdIdxRef>),
999    }
1000
1001    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1002
1003    use std::{
1004        cell::Cell,
1005        collections::{BTreeMap, HashMap},
1006        iter::FromIterator,
1007    };
1008
1009    pub(super) fn build_context(
1010        ir_ast_mod: IrAstModule,
1011        source_engine: &SourceEngine,
1012        experimental: ExperimentalFeatures,
1013    ) -> Result<Context, IrError> {
1014        let mut ctx = Context::new(source_engine, experimental);
1015        let md_map = build_metadata_map(&mut ctx, ir_ast_mod.metadata);
1016        let module = Module::new(&mut ctx, ir_ast_mod.kind);
1017        let mut builder = IrBuilder {
1018            module,
1019            configs_map: build_configs_map(&mut ctx, &module, ir_ast_mod.configs, &md_map),
1020            globals_map: build_global_vars_map(&mut ctx, &module, ir_ast_mod.global_vars),
1021            md_map,
1022            unresolved_calls: Vec::new(),
1023        };
1024
1025        for fn_decl in ir_ast_mod.fn_decls {
1026            builder.add_fn_decl(&mut ctx, fn_decl)?;
1027        }
1028
1029        builder.resolve_calls(&mut ctx)?;
1030
1031        Ok(ctx)
1032    }
1033
1034    struct IrBuilder {
1035        module: Module,
1036        configs_map: BTreeMap<String, String>,
1037        globals_map: BTreeMap<Vec<String>, GlobalVar>,
1038        md_map: HashMap<MdIdxRef, MetadataIndex>,
1039        unresolved_calls: Vec<PendingCall>,
1040    }
1041
1042    struct PendingCall {
1043        call_val: Value,
1044        callee: String,
1045    }
1046
1047    impl IrBuilder {
1048        fn add_fn_decl(
1049            &mut self,
1050            context: &mut Context,
1051            fn_decl: IrAstFnDecl,
1052        ) -> Result<(), IrError> {
1053            let convert_md_idx = |opt_md_idx: &Option<MdIdxRef>| {
1054                opt_md_idx.and_then(|mdi| self.md_map.get(&mdi).copied())
1055            };
1056            let args: Vec<(String, Type, Option<MetadataIndex>)> = fn_decl
1057                .args
1058                .iter()
1059                .map(|(ty, name, md_idx)| {
1060                    (name.into(), ty.to_ir_type(context), convert_md_idx(md_idx))
1061                })
1062                .collect();
1063            let ret_type = fn_decl.ret_type.to_ir_type(context);
1064            let func = Function::new(
1065                context,
1066                self.module,
1067                fn_decl.name,
1068                args,
1069                ret_type,
1070                fn_decl.selector,
1071                fn_decl.is_public,
1072                fn_decl.is_entry,
1073                fn_decl.is_original_entry,
1074                fn_decl.is_fallback,
1075                convert_md_idx(&fn_decl.metadata),
1076            );
1077
1078            let mut arg_map = HashMap::default();
1079            let mut local_map = HashMap::<String, LocalVar>::new();
1080            for (ty, name, initializer, mutable) in fn_decl.locals {
1081                let initializer = initializer.map(|const_init| {
1082                    if let IrAstOperation::Const(val_ty, val) = const_init {
1083                        val.value.as_constant(context, val_ty)
1084                    } else {
1085                        unreachable!("BUG! Initializer must be a const value.");
1086                    }
1087                });
1088                let ty = ty.to_ir_type(context);
1089                local_map.insert(
1090                    name.clone(),
1091                    func.new_local_var(context, name, ty, initializer, mutable)?,
1092                );
1093            }
1094
1095            // The entry block is already created, we don't want to recreate it.
1096            let named_blocks =
1097                HashMap::from_iter(fn_decl.blocks.iter().scan(true, |is_entry, block| {
1098                    Some((
1099                        block.label.clone(),
1100                        if *is_entry {
1101                            *is_entry = false;
1102                            func.get_entry_block(context)
1103                        } else {
1104                            let irblock = func.create_block(context, Some(block.label.clone()));
1105                            for (idx, (arg_ty, _, md)) in block.args.iter().enumerate() {
1106                                let ty = arg_ty.to_ir_type(context);
1107                                let arg = Value::new_argument(
1108                                    context,
1109                                    BlockArgument {
1110                                        block: irblock,
1111                                        idx,
1112                                        ty,
1113                                    },
1114                                )
1115                                .add_metadatum(context, convert_md_idx(md));
1116                                irblock.add_arg(context, arg);
1117                            }
1118                            irblock
1119                        },
1120                    ))
1121                }));
1122
1123            for block in fn_decl.blocks {
1124                for (idx, arg) in block.args.iter().enumerate() {
1125                    arg_map.insert(
1126                        arg.1.clone(),
1127                        named_blocks[&block.label].get_arg(context, idx).unwrap(),
1128                    );
1129                }
1130                self.add_block_instructions(
1131                    context,
1132                    block,
1133                    &named_blocks,
1134                    &local_map,
1135                    &mut arg_map,
1136                );
1137            }
1138            Ok(())
1139        }
1140
1141        fn add_block_instructions(
1142            &mut self,
1143            context: &mut Context,
1144            ir_block: IrAstBlock,
1145            named_blocks: &HashMap<String, Block>,
1146            local_map: &HashMap<String, LocalVar>,
1147            val_map: &mut HashMap<String, Value>,
1148        ) {
1149            let block = named_blocks.get(&ir_block.label).unwrap();
1150            for ins in ir_block.instructions {
1151                let opt_metadata = ins.metadata.and_then(|mdi| self.md_map.get(&mdi)).copied();
1152                let ins_val = match ins.op {
1153                    IrAstOperation::Asm(args, return_type, return_name, ops, meta_idx) => {
1154                        let args = args
1155                            .into_iter()
1156                            .map(|(name, opt_init)| AsmArg {
1157                                name,
1158                                initializer: opt_init.map(|init| match init {
1159                                    IrAstAsmArgInit::Var(var) => {
1160                                        val_map.get(&var).cloned().unwrap()
1161                                    }
1162                                    IrAstAsmArgInit::Imm(cv) => {
1163                                        cv.value.as_value(context, IrAstTy::U64).add_metadatum(
1164                                            context,
1165                                            self.md_map.get(cv.meta_idx.as_ref().unwrap()).copied(),
1166                                        )
1167                                    }
1168                                }),
1169                            })
1170                            .collect();
1171                        let body = ops
1172                            .into_iter()
1173                            .map(
1174                                |IrAstAsmOp {
1175                                     name,
1176                                     args,
1177                                     imm,
1178                                     meta_idx,
1179                                 }| AsmInstruction {
1180                                    op_name: name,
1181                                    args,
1182                                    immediate: imm,
1183                                    metadata: meta_idx
1184                                        .as_ref()
1185                                        .and_then(|meta_idx| self.md_map.get(meta_idx).copied()),
1186                                },
1187                            )
1188                            .collect();
1189                        let md_idx = meta_idx.map(|mdi| self.md_map.get(&mdi).unwrap()).copied();
1190                        let return_type = return_type.to_ir_type(context);
1191                        block
1192                            .append(context)
1193                            .asm_block(args, body, return_type, return_name)
1194                            .add_metadatum(context, md_idx)
1195                    }
1196                    IrAstOperation::BitCast(val, ty) => {
1197                        let to_ty = ty.to_ir_type(context);
1198                        block
1199                            .append(context)
1200                            .bitcast(*val_map.get(&val).unwrap(), to_ty)
1201                            .add_metadatum(context, opt_metadata)
1202                    }
1203                    IrAstOperation::UnaryOp(op, arg) => block
1204                        .append(context)
1205                        .unary_op(op, *val_map.get(&arg).unwrap())
1206                        .add_metadatum(context, opt_metadata),
1207                    // Wide Operations
1208                    IrAstOperation::WideUnaryOp(op, arg, result) => block
1209                        .append(context)
1210                        .wide_unary_op(
1211                            op,
1212                            *val_map.get(&arg).unwrap(),
1213                            *val_map.get(&result).unwrap(),
1214                        )
1215                        .add_metadatum(context, opt_metadata),
1216                    IrAstOperation::WideBinaryOp(op, arg1, arg2, result) => block
1217                        .append(context)
1218                        .wide_binary_op(
1219                            op,
1220                            *val_map.get(&arg1).unwrap(),
1221                            *val_map.get(&arg2).unwrap(),
1222                            *val_map.get(&result).unwrap(),
1223                        )
1224                        .add_metadatum(context, opt_metadata),
1225                    IrAstOperation::WideModularOp(op, arg1, arg2, arg3, result) => block
1226                        .append(context)
1227                        .wide_modular_op(
1228                            op,
1229                            *val_map.get(&result).unwrap(),
1230                            *val_map.get(&arg1).unwrap(),
1231                            *val_map.get(&arg2).unwrap(),
1232                            *val_map.get(&arg3).unwrap(),
1233                        )
1234                        .add_metadatum(context, opt_metadata),
1235                    IrAstOperation::WideCmp(op, arg1, arg2) => block
1236                        .append(context)
1237                        .wide_cmp_op(
1238                            op,
1239                            *val_map.get(&arg1).unwrap(),
1240                            *val_map.get(&arg2).unwrap(),
1241                        )
1242                        .add_metadatum(context, opt_metadata),
1243                    IrAstOperation::BinaryOp(op, arg1, arg2) => block
1244                        .append(context)
1245                        .binary_op(
1246                            op,
1247                            *val_map.get(&arg1).unwrap(),
1248                            *val_map.get(&arg2).unwrap(),
1249                        )
1250                        .add_metadatum(context, opt_metadata),
1251                    IrAstOperation::Br(to_block_name, args) => {
1252                        let to_block = named_blocks.get(&to_block_name).unwrap();
1253                        block
1254                            .append(context)
1255                            .branch(
1256                                *to_block,
1257                                args.iter().map(|arg| *val_map.get(arg).unwrap()).collect(),
1258                            )
1259                            .add_metadatum(context, opt_metadata)
1260                    }
1261                    IrAstOperation::Call(callee, args) => {
1262                        // We can't resolve calls to other functions until we've done a first pass and
1263                        // created them first.  So we can insert a dummy call here, save the call
1264                        // params and update it with the proper callee function in a second pass.
1265                        //
1266                        // The dummy function we'll use for now is just the current function.
1267                        let dummy_func = block.get_function(context);
1268                        let call_val = block
1269                            .append(context)
1270                            .call(
1271                                dummy_func,
1272                                &args
1273                                    .iter()
1274                                    .map(|arg_name| val_map.get(arg_name).unwrap())
1275                                    .cloned()
1276                                    .collect::<Vec<Value>>(),
1277                            )
1278                            .add_metadatum(context, opt_metadata);
1279                        self.unresolved_calls.push(PendingCall { call_val, callee });
1280                        call_val
1281                    }
1282                    IrAstOperation::CastPtr(val, ty) => {
1283                        let ir_ty = ty.to_ir_type(context);
1284                        block
1285                            .append(context)
1286                            .cast_ptr(*val_map.get(&val).unwrap(), ir_ty)
1287                            .add_metadatum(context, opt_metadata)
1288                    }
1289                    IrAstOperation::Cbr(
1290                        cond_val_name,
1291                        true_block_name,
1292                        true_args,
1293                        false_block_name,
1294                        false_args,
1295                    ) => block
1296                        .append(context)
1297                        .conditional_branch(
1298                            *val_map.get(&cond_val_name).unwrap(),
1299                            *named_blocks.get(&true_block_name).unwrap(),
1300                            *named_blocks.get(&false_block_name).unwrap(),
1301                            true_args
1302                                .iter()
1303                                .map(|arg| *val_map.get(arg).unwrap())
1304                                .collect(),
1305                            false_args
1306                                .iter()
1307                                .map(|arg| *val_map.get(arg).unwrap())
1308                                .collect(),
1309                        )
1310                        .add_metadatum(context, opt_metadata),
1311                    IrAstOperation::Cmp(pred, lhs, rhs) => block
1312                        .append(context)
1313                        .cmp(
1314                            pred,
1315                            *val_map.get(&lhs).unwrap(),
1316                            *val_map.get(&rhs).unwrap(),
1317                        )
1318                        .add_metadatum(context, opt_metadata),
1319                    IrAstOperation::Const(ty, val) => val
1320                        .value
1321                        .as_value(context, ty)
1322                        .add_metadatum(context, opt_metadata),
1323                    IrAstOperation::ContractCall(
1324                        return_type,
1325                        name,
1326                        params,
1327                        coins,
1328                        asset_id,
1329                        gas,
1330                    ) => {
1331                        let ir_ty = return_type.to_ir_type(context);
1332                        block
1333                            .append(context)
1334                            .contract_call(
1335                                ir_ty,
1336                                Some(name),
1337                                *val_map.get(&params).unwrap(),
1338                                *val_map.get(&coins).unwrap(),
1339                                *val_map.get(&asset_id).unwrap(),
1340                                *val_map.get(&gas).unwrap(),
1341                            )
1342                            .add_metadatum(context, opt_metadata)
1343                    }
1344                    IrAstOperation::GetElemPtr(base, elem_ty, idcs) => {
1345                        let ir_elem_ty = elem_ty
1346                            .to_ir_type(context)
1347                            .get_pointee_type(context)
1348                            .unwrap();
1349                        block
1350                            .append(context)
1351                            .get_elem_ptr(
1352                                *val_map.get(&base).unwrap(),
1353                                ir_elem_ty,
1354                                idcs.iter().map(|idx| *val_map.get(idx).unwrap()).collect(),
1355                            )
1356                            .add_metadatum(context, opt_metadata)
1357                    }
1358                    IrAstOperation::GetLocal(local_name) => block
1359                        .append(context)
1360                        .get_local(*local_map.get(&local_name).unwrap())
1361                        .add_metadatum(context, opt_metadata),
1362                    IrAstOperation::GetGlobal(global_name) => block
1363                        .append(context)
1364                        .get_global(*self.globals_map.get(&global_name).unwrap())
1365                        .add_metadatum(context, opt_metadata),
1366                    IrAstOperation::GetConfig(name) => block
1367                        .append(context)
1368                        .get_config(self.module, name)
1369                        .add_metadatum(context, opt_metadata),
1370                    IrAstOperation::Gtf(index, tx_field_id) => block
1371                        .append(context)
1372                        .gtf(*val_map.get(&index).unwrap(), tx_field_id)
1373                        .add_metadatum(context, opt_metadata),
1374                    IrAstOperation::IntToPtr(val, ty) => {
1375                        let to_ty = ty.to_ir_type(context);
1376                        block
1377                            .append(context)
1378                            .int_to_ptr(*val_map.get(&val).unwrap(), to_ty)
1379                            .add_metadatum(context, opt_metadata)
1380                    }
1381                    IrAstOperation::Load(src_name) => block
1382                        .append(context)
1383                        .load(*val_map.get(&src_name).unwrap())
1384                        .add_metadatum(context, opt_metadata),
1385                    IrAstOperation::Log(log_ty, log_val, log_id) => {
1386                        let log_ty = log_ty.to_ir_type(context);
1387                        block
1388                            .append(context)
1389                            .log(
1390                                *val_map.get(&log_val).unwrap(),
1391                                log_ty,
1392                                *val_map.get(&log_id).unwrap(),
1393                            )
1394                            .add_metadatum(context, opt_metadata)
1395                    }
1396                    IrAstOperation::MemCopyBytes(dst_name, src_name, len) => block
1397                        .append(context)
1398                        .mem_copy_bytes(
1399                            *val_map.get(&dst_name).unwrap(),
1400                            *val_map.get(&src_name).unwrap(),
1401                            len,
1402                        )
1403                        .add_metadatum(context, opt_metadata),
1404                    IrAstOperation::MemCopyVal(dst_name, src_name) => block
1405                        .append(context)
1406                        .mem_copy_val(
1407                            *val_map.get(&dst_name).unwrap(),
1408                            *val_map.get(&src_name).unwrap(),
1409                        )
1410                        .add_metadatum(context, opt_metadata),
1411                    IrAstOperation::Nop => block.append(context).nop(),
1412                    IrAstOperation::PtrToInt(val, ty) => {
1413                        let to_ty = ty.to_ir_type(context);
1414                        block
1415                            .append(context)
1416                            .ptr_to_int(*val_map.get(&val).unwrap(), to_ty)
1417                            .add_metadatum(context, opt_metadata)
1418                    }
1419                    IrAstOperation::ReadRegister(reg_name) => block
1420                        .append(context)
1421                        .read_register(match reg_name.as_str() {
1422                            "of" => Register::Of,
1423                            "pc" => Register::Pc,
1424                            "ssp" => Register::Ssp,
1425                            "sp" => Register::Sp,
1426                            "fp" => Register::Fp,
1427                            "hp" => Register::Hp,
1428                            "err" => Register::Error,
1429                            "ggas" => Register::Ggas,
1430                            "cgas" => Register::Cgas,
1431                            "bal" => Register::Bal,
1432                            "is" => Register::Is,
1433                            "ret" => Register::Ret,
1434                            "retl" => Register::Retl,
1435                            "flag" => Register::Flag,
1436                            _ => unreachable!("Guaranteed by grammar."),
1437                        })
1438                        .add_metadatum(context, opt_metadata),
1439                    IrAstOperation::Ret(ty, ret_val_name) => {
1440                        let ty = ty.to_ir_type(context);
1441                        block
1442                            .append(context)
1443                            .ret(*val_map.get(&ret_val_name).unwrap(), ty)
1444                            .add_metadatum(context, opt_metadata)
1445                    }
1446                    IrAstOperation::Revert(ret_val_name) => block
1447                        .append(context)
1448                        .revert(*val_map.get(&ret_val_name).unwrap())
1449                        .add_metadatum(context, opt_metadata),
1450                    IrAstOperation::JmpMem => block
1451                        .append(context)
1452                        .jmp_mem()
1453                        .add_metadatum(context, opt_metadata),
1454                    IrAstOperation::Smo(recipient, message, message_size, coins) => block
1455                        .append(context)
1456                        .smo(
1457                            *val_map.get(&recipient).unwrap(),
1458                            *val_map.get(&message).unwrap(),
1459                            *val_map.get(&message_size).unwrap(),
1460                            *val_map.get(&coins).unwrap(),
1461                        )
1462                        .add_metadatum(context, opt_metadata),
1463                    IrAstOperation::StateClear(key, number_of_slots) => block
1464                        .append(context)
1465                        .state_clear(
1466                            *val_map.get(&key).unwrap(),
1467                            *val_map.get(&number_of_slots).unwrap(),
1468                        )
1469                        .add_metadatum(context, opt_metadata),
1470                    IrAstOperation::StateLoadQuadWord(dst, key, number_of_slots) => block
1471                        .append(context)
1472                        .state_load_quad_word(
1473                            *val_map.get(&dst).unwrap(),
1474                            *val_map.get(&key).unwrap(),
1475                            *val_map.get(&number_of_slots).unwrap(),
1476                        )
1477                        .add_metadatum(context, opt_metadata),
1478                    IrAstOperation::StateLoadWord(key) => block
1479                        .append(context)
1480                        .state_load_word(*val_map.get(&key).unwrap())
1481                        .add_metadatum(context, opt_metadata),
1482                    IrAstOperation::StateStoreQuadWord(src, key, number_of_slots) => block
1483                        .append(context)
1484                        .state_store_quad_word(
1485                            *val_map.get(&src).unwrap(),
1486                            *val_map.get(&key).unwrap(),
1487                            *val_map.get(&number_of_slots).unwrap(),
1488                        )
1489                        .add_metadatum(context, opt_metadata),
1490                    IrAstOperation::StateStoreWord(src, key) => block
1491                        .append(context)
1492                        .state_store_word(*val_map.get(&src).unwrap(), *val_map.get(&key).unwrap())
1493                        .add_metadatum(context, opt_metadata),
1494                    IrAstOperation::Store(stored_val_name, dst_val_name) => {
1495                        let dst_val_ptr = *val_map.get(&dst_val_name).unwrap();
1496                        let stored_val = *val_map.get(&stored_val_name).unwrap();
1497
1498                        block
1499                            .append(context)
1500                            .store(dst_val_ptr, stored_val)
1501                            .add_metadatum(context, opt_metadata)
1502                    }
1503                };
1504                ins.value_name.map(|vn| val_map.insert(vn, ins_val));
1505            }
1506        }
1507
1508        fn resolve_calls(self, context: &mut Context) -> Result<(), IrError> {
1509            for (configurable_name, fn_name) in self.configs_map {
1510                let f = self
1511                    .module
1512                    .function_iter(context)
1513                    .find(|x| x.get_name(context) == fn_name)
1514                    .unwrap();
1515
1516                if let Some(ConfigContent::V1 { decode_fn, .. }) = context
1517                    .modules
1518                    .get_mut(self.module.0)
1519                    .unwrap()
1520                    .configs
1521                    .get_mut(&configurable_name)
1522                {
1523                    decode_fn.replace(f);
1524                }
1525            }
1526
1527            // All of the call instructions are currently invalid (recursive) CALLs to their own
1528            // function, which need to be replaced with the proper callee function.  We couldn't do
1529            // it above until we'd gone and created all the functions first.
1530            //
1531            // Now we can loop and find the callee function for each call and update them.
1532            for pending_call in self.unresolved_calls {
1533                let call_func = context
1534                    .functions
1535                    .iter()
1536                    .find_map(|(idx, content)| {
1537                        if content.name == pending_call.callee {
1538                            Some(Function(idx))
1539                        } else {
1540                            None
1541                        }
1542                    })
1543                    .unwrap();
1544
1545                if let Some(Instruction {
1546                    op: InstOp::Call(dummy_func, _args),
1547                    ..
1548                }) = pending_call.call_val.get_instruction_mut(context)
1549                {
1550                    *dummy_func = call_func;
1551                }
1552            }
1553            Ok(())
1554        }
1555    }
1556
1557    fn build_global_vars_map(
1558        context: &mut Context,
1559        module: &Module,
1560        global_vars: Vec<IrAstGlobalVar>,
1561    ) -> BTreeMap<Vec<String>, GlobalVar> {
1562        global_vars
1563            .into_iter()
1564            .map(|global_var_node| {
1565                let ty = global_var_node.ty.to_ir_type(context);
1566                let init = global_var_node.init.map(|init| match init {
1567                    IrAstOperation::Const(ty, val) => val.value.as_constant(context, ty),
1568                    _ => unreachable!("Global const initializer must be a const value."),
1569                });
1570                let global_var = GlobalVar::new(context, ty, init, global_var_node.mutable);
1571                module.add_global_variable(context, global_var_node.name.clone(), global_var);
1572                (global_var_node.name, global_var)
1573            })
1574            .collect()
1575    }
1576
1577    fn build_configs_map(
1578        context: &mut Context,
1579        module: &Module,
1580        configs: Vec<IrAstConfig>,
1581        md_map: &HashMap<MdIdxRef, MetadataIndex>,
1582    ) -> BTreeMap<String, String> {
1583        configs
1584            .into_iter()
1585            .map(|config| {
1586                let opt_metadata = config
1587                    .metadata
1588                    .map(|mdi| md_map.get(&mdi).unwrap())
1589                    .copied();
1590
1591                let ty = config.ty.to_ir_type(context);
1592
1593                let config_val = ConfigContent::V1 {
1594                    name: config.value_name.clone(),
1595                    ty,
1596                    ptr_ty: Type::new_ptr(context, ty),
1597                    encoded_bytes: config.encoded_bytes,
1598                    // this will point to the correct function after all functions are compiled
1599                    decode_fn: Cell::new(Function(KeyData::default().into())),
1600                    opt_metadata,
1601                };
1602
1603                module.add_config(context, config.value_name.clone(), config_val.clone());
1604
1605                (config.value_name.clone(), config.decode_fn.clone())
1606            })
1607            .collect()
1608    }
1609
1610    /// Create the metadata for the module in `context` and generate a map from the parsed
1611    /// `MdIdxRef`s to the new actual metadata.
1612    fn build_metadata_map(
1613        context: &mut Context,
1614        ir_metadata: Vec<(MdIdxRef, IrMetadatum)>,
1615    ) -> HashMap<MdIdxRef, MetadataIndex> {
1616        fn convert_md(md: IrMetadatum, md_map: &mut HashMap<MdIdxRef, MetadataIndex>) -> Metadatum {
1617            match md {
1618                IrMetadatum::Integer(i) => Metadatum::Integer(i),
1619                IrMetadatum::Index(idx) => Metadatum::Index(
1620                    md_map
1621                        .get(&idx)
1622                        .copied()
1623                        .expect("Metadatum index not found in map."),
1624                ),
1625                IrMetadatum::String(s) => Metadatum::String(s),
1626                IrMetadatum::Struct(tag, els) => Metadatum::Struct(
1627                    tag,
1628                    els.into_iter()
1629                        .map(|el_md| convert_md(el_md, md_map))
1630                        .collect(),
1631                ),
1632                IrMetadatum::List(idcs) => Metadatum::List(
1633                    idcs.into_iter()
1634                        .map(|idx| {
1635                            md_map
1636                                .get(&idx)
1637                                .copied()
1638                                .expect("Metadatum index not found in map.")
1639                        })
1640                        .collect(),
1641                ),
1642            }
1643        }
1644
1645        let mut md_map = HashMap::new();
1646
1647        for (ir_idx, ir_md) in ir_metadata {
1648            let md = convert_md(ir_md, &mut md_map);
1649            let md_idx = MetadataIndex(context.metadata.insert(md));
1650            md_map.insert(ir_idx, md_idx);
1651        }
1652        md_map
1653    }
1654
1655    fn string_to_hex<const N: usize>(s: &str) -> [u8; N] {
1656        let mut bytes: [u8; N] = [0; N];
1657        let mut cur_byte: u8 = 0;
1658        for (idx, ch) in s.chars().enumerate() {
1659            cur_byte = (cur_byte << 4) | ch.to_digit(16).unwrap() as u8;
1660            if idx % 2 == 1 {
1661                bytes[idx / 2] = cur_byte;
1662                cur_byte = 0;
1663            }
1664        }
1665        bytes
1666    }
1667
1668    fn hex_string_to_vec(s: &str) -> Vec<u8> {
1669        let mut bytes = vec![];
1670        let mut cur_byte: u8 = 0;
1671        for (idx, ch) in s.chars().enumerate() {
1672            cur_byte = (cur_byte << 4) | ch.to_digit(16).unwrap() as u8;
1673            if idx % 2 == 1 {
1674                bytes.push(cur_byte);
1675                cur_byte = 0;
1676            }
1677        }
1678        bytes
1679    }
1680}
1681
1682// -------------------------------------------------------------------------------------------------