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