Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ Cargo.lock
.vscode/

/test_snapshots
/llvm16.0*
3 changes: 2 additions & 1 deletion src/bin/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ pub struct TargetArg {

#[derive(Args, Deserialize, Debug, PartialEq)]
pub struct CompileTargetArg {
#[arg(name = "TARGET", long = "target", value_parser = ["solana", "polkadot", "evm", "soroban"], help = "Target to build for [possible values: solana, polkadot]", num_args = 1, hide_possible_values = true)]
#[arg(name = "TARGET", long = "target", value_parser = ["solana", "polkadot", "evm", "soroban", "stylus"], help = "Target to build for [possible values: solana, polkadot, stylus]", num_args = 1, hide_possible_values = true)]
pub name: Option<String>,

#[arg(name = "ADDRESS_LENGTH", help = "Address length on the Polkadot Parachain", long = "address-length", num_args = 1, value_parser = value_parser!(u64).range(4..1024))]
Expand Down Expand Up @@ -464,6 +464,7 @@ pub(crate) fn target_arg<T: TargetArgTrait>(target_arg: &T) -> Target {
},
"evm" => solang::Target::EVM,
"soroban" => solang::Target::Soroban,
"stylus" => solang::Target::Stylus,
_ => unreachable!(),
};

Expand Down
2 changes: 2 additions & 0 deletions src/codegen/dispatch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::{sema::ast::Namespace, Target};
pub(crate) mod polkadot;
pub(super) mod solana;
pub(super) mod soroban;
pub(super) mod stylus;

pub(super) fn function_dispatch(
contract_no: usize,
Expand All @@ -19,5 +20,6 @@ pub(super) fn function_dispatch(
polkadot::function_dispatch(contract_no, all_cfg, ns, opt)
}
Target::Soroban => soroban::function_dispatch(contract_no, all_cfg, ns, opt),
Target::Stylus => stylus::function_dispatch(contract_no, all_cfg, ns, opt),
}
}
6 changes: 3 additions & 3 deletions src/codegen/dispatch/polkadot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub(crate) fn function_dispatch(
]
}

struct Dispatch<'a> {
pub(super) struct Dispatch<'a> {
start: usize,
input_len: usize,
input_ptr: Expression,
Expand Down Expand Up @@ -110,7 +110,7 @@ impl<'a> Dispatch<'a> {
/// Create a new `Dispatch` struct that has all the data needed for building the dispatch logic.
///
/// `ty` specifies whether to include constructors or functions.
fn new(
pub(super) fn new(
all_cfg: &'a [ControlFlowGraph],
ns: &'a mut Namespace,
opt: &'a Options,
Expand Down Expand Up @@ -195,7 +195,7 @@ impl<'a> Dispatch<'a> {
}

/// Build the dispatch logic into the returned control flow graph.
fn build(mut self) -> ControlFlowGraph {
pub(super) fn build(mut self) -> ControlFlowGraph {
// Go to fallback or receive if there is no selector in the call input
let cond = Expression::Less {
loc: Codegen,
Expand Down
17 changes: 17 additions & 0 deletions src/codegen/dispatch/stylus.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Apache-2.0

use super::polkadot;
use crate::{
codegen::{cfg::ControlFlowGraph, Options},
sema::ast::Namespace,
};
use solang_parser::pt::FunctionTy;

pub(crate) fn function_dispatch(
_contract_no: usize,
all_cfg: &[ControlFlowGraph],
ns: &mut Namespace,
opt: &Options,
) -> Vec<ControlFlowGraph> {
vec![polkadot::Dispatch::new(all_cfg, ns, opt, FunctionTy::Function).build()]
}
2 changes: 1 addition & 1 deletion src/codegen/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub(super) fn new_event_emitter<'a>(
ns: &'a Namespace,
) -> Box<dyn EventEmitter + 'a> {
match ns.target {
Target::Polkadot { .. } | Target::EVM => {
Target::Polkadot { .. } | Target::EVM | Target::Stylus => {
Box::new(PolkadotEventEmitter { args, ns, event_no })
}

Expand Down
7 changes: 6 additions & 1 deletion src/emit/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use tempfile::tempdir;
use wasm_opt::OptimizationOptions;

use crate::codegen::{cfg::ReturnCode, Options};
use crate::emit::stylus;
use crate::emit::{polkadot, TargetRuntime};
use crate::emit::{solana, BinaryOp, Generate};
use crate::linker::link;
Expand Down Expand Up @@ -52,7 +53,10 @@ macro_rules! emit_context {
#[allow(unused_macros)]
macro_rules! byte_ptr {
() => {
$binary.context.i8_type().ptr_type(AddressSpace::default())
$binary
.context
.i8_type()
.ptr_type(inkwell::AddressSpace::default())
};
}

Expand Down Expand Up @@ -191,6 +195,7 @@ impl<'a> Binary<'a> {
Target::Soroban => {
soroban::SorobanTarget::build(context, &std_lib, contract, ns, opt, _contract_no)
}
Target::Stylus => stylus::StylusTarget::build(context, &std_lib, contract, ns, opt),
_ => unimplemented!("target not implemented"),
}
}
Expand Down
1 change: 1 addition & 0 deletions src/emit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ mod loop_builder;
mod math;
pub mod polkadot;
pub mod solana;
pub mod stylus;

#[cfg(feature = "soroban")]
pub mod soroban;
Expand Down
142 changes: 142 additions & 0 deletions src/emit/stylus/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// SPDX-License-Identifier: Apache-2.0

use crate::codegen::dispatch::polkadot::DispatchType;
use crate::codegen::Options;
use crate::emit::functions::emit_functions;
use crate::emit::Binary;
use crate::emit_context;
use crate::sema::ast::{Contract, Namespace};
use inkwell::context::Context;
use inkwell::module::{Linkage, Module};
use inkwell::values::{BasicMetadataValueEnum, FunctionValue, IntValue, PointerValue};
use inkwell::AddressSpace;

mod target;

pub struct StylusTarget;

impl StylusTarget {
pub fn build<'a>(
context: &'a Context,
std_lib: &Module<'a>,
contract: &'a Contract,
ns: &'a Namespace,
opt: &'a Options,
) -> Binary<'a> {
let filename = ns.files[contract.loc.file_no()].file_name();
let mut binary = Binary::new(
context,
ns.target,
&contract.id.name,
filename.as_str(),
opt,
std_lib,
None,
);

let mut target = StylusTarget;

target.declare_externals(&binary);

emit_functions(&mut target, &mut binary, contract, ns);

target.emit_dispatch(&mut binary, ns);

binary.internalize(&[
"log_txt",
"msg_reentrant",
"msg_value",
"pay_for_memory_grow",
"read_args",
"storage_flush_cache",
"storage_cache_bytes32",
"storage_load_bytes32",
"write_result",
]);

binary
}

fn public_function_prelude<'a>(
&self,
binary: &Binary<'a>,
function: FunctionValue<'a>,
) -> (PointerValue<'a>, IntValue<'a>) {
emit_context!(binary);

let entry = binary.context.append_basic_block(function, "entry");

binary.builder.position_at_end(entry);

// init our heap
binary
.builder
.build_call(
binary.module.get_function("__init_heap").unwrap(),
&[],
"__init_heap",
)
.unwrap();

let args_len = function.get_nth_param(0).unwrap();

let args = call!("__malloc", &[args_len.into()], "__malloc")
.try_as_basic_value()
.left()
.unwrap();

call!("read_args", &[args.into()], "read_args");

(args.into_pointer_value(), args_len.into_int_value())
}

fn declare_externals(&self, binary: &Binary) {
let ctx = binary.context;
let i32_val = ctx.i32_type().into();
let u8_ptr = ctx.i8_type().ptr_type(AddressSpace::default()).into();

macro_rules! external {
($name:literal, $fn_type:ident $(,)? $( $args:expr ),*) => {
binary.module.add_function(
$name,
ctx.$fn_type().fn_type(&[$($args),*], false),
Some(Linkage::External),
);
};
}

external!("log_txt", void_type, u8_ptr, i32_val);
external!("msg_reentrant", i32_type);
external!("msg_value", void_type, i32_val);
external!("pay_for_memory_grow", void_type, i32_val);
external!("read_args", void_type, u8_ptr);
external!("storage_cache_bytes32", void_type, u8_ptr, u8_ptr);
external!("storage_flush_cache", void_type, i32_val);
external!("storage_load_bytes32", void_type, u8_ptr, u8_ptr);
external!("write_result", void_type, u8_ptr, i32_val);
}

fn emit_dispatch(&mut self, bin: &mut Binary, _ns: &Namespace) {
let ty = bin
.context
.i32_type()
.fn_type(&[bin.context.i32_type().into()], false);
let func = bin.module.add_function("user_entrypoint", ty, None);
let (args, args_len) = self.public_function_prelude(bin, func);
// smoelius: FIXME: zero
let zero = bin.context.custom_width_int_type(256).const_zero();
let args = &[
BasicMetadataValueEnum::PointerValue(args),
BasicMetadataValueEnum::IntValue(args_len),
BasicMetadataValueEnum::IntValue(zero),
BasicMetadataValueEnum::PointerValue(bin.selector.as_pointer_value()),
];
let dispatch_cfg_name = &DispatchType::Call.to_string();
let cfg = bin.module.get_function(dispatch_cfg_name).unwrap();
bin.builder
.build_call(cfg, args, dispatch_cfg_name)
.unwrap();

bin.builder.build_unreachable().unwrap();
}
}
Loading
Loading