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