|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | + |
| 3 | +use crate::codegen::dispatch::polkadot::DispatchType; |
| 4 | +use crate::codegen::Options; |
| 5 | +use crate::emit::functions::emit_functions; |
| 6 | +use crate::emit::Binary; |
| 7 | +use crate::emit_context; |
| 8 | +use crate::sema::ast::{Contract, Namespace}; |
| 9 | +use inkwell::context::Context; |
| 10 | +use inkwell::module::{Linkage, Module}; |
| 11 | +use inkwell::values::{BasicMetadataValueEnum, FunctionValue, IntValue, PointerValue}; |
| 12 | +use inkwell::AddressSpace; |
| 13 | + |
| 14 | +mod target; |
| 15 | + |
| 16 | +pub struct StylusTarget; |
| 17 | + |
| 18 | +impl StylusTarget { |
| 19 | + pub fn build<'a>( |
| 20 | + context: &'a Context, |
| 21 | + std_lib: &Module<'a>, |
| 22 | + contract: &'a Contract, |
| 23 | + ns: &'a Namespace, |
| 24 | + opt: &'a Options, |
| 25 | + ) -> Binary<'a> { |
| 26 | + let filename = ns.files[contract.loc.file_no()].file_name(); |
| 27 | + let mut binary = Binary::new( |
| 28 | + context, |
| 29 | + ns.target, |
| 30 | + &contract.id.name, |
| 31 | + filename.as_str(), |
| 32 | + opt, |
| 33 | + std_lib, |
| 34 | + None, |
| 35 | + ); |
| 36 | + |
| 37 | + let mut target = StylusTarget; |
| 38 | + |
| 39 | + target.declare_externals(&binary); |
| 40 | + |
| 41 | + emit_functions(&mut target, &mut binary, contract, ns); |
| 42 | + |
| 43 | + target.emit_dispatch(&mut binary, ns); |
| 44 | + |
| 45 | + binary.internalize(&[ |
| 46 | + "log_txt", |
| 47 | + "msg_reentrant", |
| 48 | + "msg_value", |
| 49 | + "pay_for_memory_grow", |
| 50 | + "read_args", |
| 51 | + "storage_flush_cache", |
| 52 | + "storage_cache_bytes32", |
| 53 | + "storage_load_bytes32", |
| 54 | + "write_result", |
| 55 | + ]); |
| 56 | + |
| 57 | + binary |
| 58 | + } |
| 59 | + |
| 60 | + fn public_function_prelude<'a>( |
| 61 | + &self, |
| 62 | + binary: &Binary<'a>, |
| 63 | + function: FunctionValue<'a>, |
| 64 | + ) -> (PointerValue<'a>, IntValue<'a>) { |
| 65 | + emit_context!(binary); |
| 66 | + |
| 67 | + let entry = binary.context.append_basic_block(function, "entry"); |
| 68 | + |
| 69 | + binary.builder.position_at_end(entry); |
| 70 | + |
| 71 | + // init our heap |
| 72 | + binary |
| 73 | + .builder |
| 74 | + .build_call( |
| 75 | + binary.module.get_function("__init_heap").unwrap(), |
| 76 | + &[], |
| 77 | + "__init_heap", |
| 78 | + ) |
| 79 | + .unwrap(); |
| 80 | + |
| 81 | + let args_len = function.get_nth_param(0).unwrap(); |
| 82 | + |
| 83 | + let args = call!("__malloc", &[args_len.into()], "__malloc") |
| 84 | + .try_as_basic_value() |
| 85 | + .left() |
| 86 | + .unwrap(); |
| 87 | + |
| 88 | + call!("read_args", &[args.into()], "read_args"); |
| 89 | + |
| 90 | + (args.into_pointer_value(), args_len.into_int_value()) |
| 91 | + } |
| 92 | + |
| 93 | + fn declare_externals(&self, binary: &Binary) { |
| 94 | + let ctx = binary.context; |
| 95 | + let i32_val = ctx.i32_type().into(); |
| 96 | + let u8_ptr = ctx.i8_type().ptr_type(AddressSpace::default()).into(); |
| 97 | + |
| 98 | + macro_rules! external { |
| 99 | + ($name:literal, $fn_type:ident $(,)? $( $args:expr ),*) => { |
| 100 | + binary.module.add_function( |
| 101 | + $name, |
| 102 | + ctx.$fn_type().fn_type(&[$($args),*], false), |
| 103 | + Some(Linkage::External), |
| 104 | + ); |
| 105 | + }; |
| 106 | + } |
| 107 | + |
| 108 | + external!("log_txt", void_type, u8_ptr, i32_val); |
| 109 | + external!("msg_reentrant", i32_type); |
| 110 | + external!("msg_value", void_type, i32_val); |
| 111 | + external!("pay_for_memory_grow", void_type, i32_val); |
| 112 | + external!("read_args", void_type, u8_ptr); |
| 113 | + external!("storage_cache_bytes32", void_type, u8_ptr, u8_ptr); |
| 114 | + external!("storage_flush_cache", void_type, i32_val); |
| 115 | + external!("storage_load_bytes32", void_type, u8_ptr, u8_ptr); |
| 116 | + external!("write_result", void_type, u8_ptr, i32_val); |
| 117 | + } |
| 118 | + |
| 119 | + fn emit_dispatch(&mut self, bin: &mut Binary, _ns: &Namespace) { |
| 120 | + let ty = bin |
| 121 | + .context |
| 122 | + .i32_type() |
| 123 | + .fn_type(&[bin.context.i32_type().into()], false); |
| 124 | + let func = bin.module.add_function("user_entrypoint", ty, None); |
| 125 | + let (args, args_len) = self.public_function_prelude(bin, func); |
| 126 | + // smoelius: FIXME: zero |
| 127 | + let zero = bin.context.custom_width_int_type(256).const_zero(); |
| 128 | + let args = &[ |
| 129 | + BasicMetadataValueEnum::PointerValue(args), |
| 130 | + BasicMetadataValueEnum::IntValue(args_len), |
| 131 | + BasicMetadataValueEnum::IntValue(zero), |
| 132 | + BasicMetadataValueEnum::PointerValue(bin.selector.as_pointer_value()), |
| 133 | + ]; |
| 134 | + let dispatch_cfg_name = &DispatchType::Call.to_string(); |
| 135 | + let cfg = bin.module.get_function(dispatch_cfg_name).unwrap(); |
| 136 | + bin.builder |
| 137 | + .build_call(cfg, args, dispatch_cfg_name) |
| 138 | + .unwrap(); |
| 139 | + |
| 140 | + bin.builder.build_unreachable().unwrap(); |
| 141 | + } |
| 142 | +} |
0 commit comments