Skip to content
Merged
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 .fdignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
testdata/solidity
22 changes: 22 additions & 0 deletions integration/stylus/milestone_3.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,28 @@ contract C {
return (balance, codehash, manual_codehash, gasprice);
}

function test_addmod() public pure {
uint256 x = addmod(500, 100, 3);
uint256 y = addmod(500, 100, 7);

print("x = {}".format(x));
print("y = {}".format(y));

assert(x == 0);
assert(y == 5);
}

function test_mulmod() public pure {
uint256 x = mulmod(500, 100, 3);
uint256 y = mulmod(500, 100, 7);

print("x = {}".format(x));
print("y = {}".format(y));

assert(x == 2);
assert(y == 6);
}

function getCode() public view returns (bytes) {
return address(this).code;
}
Expand Down
126 changes: 101 additions & 25 deletions src/emit/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1757,12 +1757,45 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
args,
..
} => {
let arith_ty = bin.context.custom_width_int_type(512);
let res_ty = bin.context.custom_width_int_type(256);

let x = expression(target, bin, &args[0], vartab, function).into_int_value();
let y = expression(target, bin, &args[1], vartab, function).into_int_value();
let k = expression(target, bin, &args[2], vartab, function).into_int_value();

if bin.ns.target == Target::Stylus {
let x_unswapped_ptr =
bin.build_alloca(function, bin.value_type(), "x_unswapped_ptr");
let y_unswapped_ptr =
bin.build_alloca(function, bin.value_type(), "y_unswapped_ptr");
let k_unswapped_ptr =
bin.build_alloca(function, bin.value_type(), "k_unswapped_ptr");
let x_swapped_ptr = bin.build_alloca(function, bin.value_type(), "x_swapped_ptr");
let y_swapped_ptr = bin.build_alloca(function, bin.value_type(), "y_swapped_ptr");
let k_swapped_ptr = bin.build_alloca(function, bin.value_type(), "k_swapped_ptr");
bin.builder.build_store(x_unswapped_ptr, x).unwrap();
bin.builder.build_store(y_unswapped_ptr, y).unwrap();
bin.builder.build_store(k_unswapped_ptr, k).unwrap();
byte_swap_value(bin, &Type::Uint(256), x_unswapped_ptr, x_swapped_ptr, true);
byte_swap_value(bin, &Type::Uint(256), y_unswapped_ptr, y_swapped_ptr, true);
byte_swap_value(bin, &Type::Uint(256), k_unswapped_ptr, k_swapped_ptr, true);
call!(
"math_add_mod",
&[
x_swapped_ptr.into(),
y_swapped_ptr.into(),
k_swapped_ptr.into()
]
);
let sum_ptr = bin.build_alloca(function, bin.value_type(), "sum_ptr");
byte_swap_value(bin, &Type::Uint(256), x_swapped_ptr, sum_ptr, false);
return bin
.builder
.build_load(bin.value_type(), sum_ptr, "sum")
.unwrap();
}

let arith_ty = bin.context.custom_width_int_type(512);
let res_ty = bin.context.custom_width_int_type(256);

let dividend = bin
.builder
.build_int_add(
Expand Down Expand Up @@ -1855,11 +1888,45 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
args,
..
} => {
let x = expression(target, bin, &args[0], vartab, function).into_int_value();
let y = expression(target, bin, &args[1], vartab, function).into_int_value();
let k = expression(target, bin, &args[2], vartab, function).into_int_value();

if bin.ns.target == Target::Stylus {
let x_unswapped_ptr =
bin.build_alloca(function, bin.value_type(), "x_unswapped_ptr");
let y_unswapped_ptr =
bin.build_alloca(function, bin.value_type(), "y_unswapped_ptr");
let k_unswapped_ptr =
bin.build_alloca(function, bin.value_type(), "k_unswapped_ptr");
let x_swapped_ptr = bin.build_alloca(function, bin.value_type(), "x_swapped_ptr");
let y_swapped_ptr = bin.build_alloca(function, bin.value_type(), "y_swapped_ptr");
let k_swapped_ptr = bin.build_alloca(function, bin.value_type(), "k_swapped_ptr");
bin.builder.build_store(x_unswapped_ptr, x).unwrap();
bin.builder.build_store(y_unswapped_ptr, y).unwrap();
bin.builder.build_store(k_unswapped_ptr, k).unwrap();
byte_swap_value(bin, &Type::Uint(256), x_unswapped_ptr, x_swapped_ptr, true);
byte_swap_value(bin, &Type::Uint(256), y_unswapped_ptr, y_swapped_ptr, true);
byte_swap_value(bin, &Type::Uint(256), k_unswapped_ptr, k_swapped_ptr, true);
call!(
"math_mul_mod",
&[
x_swapped_ptr.into(),
y_swapped_ptr.into(),
k_swapped_ptr.into()
]
);
let product_ptr = bin.build_alloca(function, bin.value_type(), "product_ptr");
byte_swap_value(bin, &Type::Uint(256), x_swapped_ptr, product_ptr, false);
return bin
.builder
.build_load(bin.value_type(), product_ptr, "product")
.unwrap();
}

let arith_ty = bin.context.custom_width_int_type(512);
let res_ty = bin.context.custom_width_int_type(256);

let x = expression(target, bin, &args[0], vartab, function).into_int_value();
let y = expression(target, bin, &args[1], vartab, function).into_int_value();
let x_m = bin.build_alloca(function, arith_ty, "x_m");
let y_m = bin.build_alloca(function, arith_ty, "x_y");
let x_times_y_m = bin.build_alloca(function, arith_ty, "x_times_y_m");
Expand Down Expand Up @@ -1893,7 +1960,6 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
"",
)
.unwrap();
let k = expression(target, bin, &args[2], vartab, function).into_int_value();
let dividend = bin
.builder
.build_load(arith_ty, x_times_y_m, "x_t_y")
Expand Down Expand Up @@ -2138,7 +2204,7 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(

Expression::ByteSwap { expr, le_to_be } => {
let ty = expr.ty();
bytes_swap(target, bin, &ty, expr, *le_to_be, vartab, function)
byte_swap_expression(target, bin, &ty, expr, *le_to_be, vartab, function)
}

Expression::RationalNumberLiteral { .. }
Expand Down Expand Up @@ -2696,7 +2762,7 @@ fn basic_value_to_slice<'a>(

// smoelius: I think this type-walking must be done during the emit phase and not during the codegen
// phase. For example, the bounds on a dynamic array cannot be known. Hence, a loop must be emitted.
fn bytes_swap<'a, T: TargetRuntime<'a> + ?Sized>(
fn byte_swap_expression<'a, T: TargetRuntime<'a> + ?Sized>(
target: &T,
bin: &Binary<'a>,
ty: &Type,
Expand All @@ -2723,31 +2789,41 @@ fn bytes_swap<'a, T: TargetRuntime<'a> + ?Sized>(
"dest",
);

let name = if le_to_be { "__leNtobeN" } else { "__beNtoleN" };

bin.builder
.build_call(
bin.module.get_function(name).unwrap(),
&[
src.into(),
dest.into(),
bin.context
.i32_type()
.const_int((ty.get_type_size() / 8) as u64, false)
.into(),
],
name,
)
.unwrap();
byte_swap_value(bin, ty, src, dest, le_to_be);

bin.builder
.build_load(llvm_ty, dest, "swapped bytes")
.unwrap()
}
Type::UserType(no) => {
let ty = &bin.ns.user_types[*no].ty;
bytes_swap(target, bin, ty, expr, le_to_be, vartab, function)
byte_swap_expression(target, bin, ty, expr, le_to_be, vartab, function)
}
_ => unimplemented!("{ty:?}"),
}
}

fn byte_swap_value(
bin: &Binary<'_>,
ty: &Type,
from: PointerValue<'_>,
to: PointerValue<'_>,
le_to_be: bool,
) {
let name = if le_to_be { "__leNtobeN" } else { "__beNtoleN" };

bin.builder
.build_call(
bin.module.get_function(name).unwrap(),
&[
from.into(),
to.into(),
bin.context
.i32_type()
.const_int((ty.get_type_size() / 8) as u64, false)
.into(),
],
name,
)
.unwrap();
}
2 changes: 1 addition & 1 deletion src/emit/stylus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ impl StylusTarget {
external!("math_add_mod", void_type, u8_ptr, u8_ptr, u8_ptr);
external!("math_div", void_type, u8_ptr, u8_ptr);
external!("math_mod", void_type, u8_ptr, u8_ptr);
external!("math_mul_mod", void_type, u8_ptr, u8_ptr);
external!("math_mul_mod", void_type, u8_ptr, u8_ptr, u8_ptr);
external!("math_pow", void_type, u8_ptr, u8_ptr);
external!("msg_sender", void_type, u8_ptr);
external!("msg_value", void_type, u8_ptr);
Expand Down
8 changes: 6 additions & 2 deletions tests/stylus_tests/milestone_3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fn milestone_3() {
let stdout = send(dir, &address, ["accept_donation()", "--value=1000"]).unwrap();
println!("{}", &stdout);

let mut labeled_stdout = call(
let stdout = call(
dir,
&address,
[
Expand All @@ -32,7 +32,7 @@ fn milestone_3() {
],
)
.unwrap();
labeled_stdout = label(&labeled_stdout);
let labeled_stdout = label(&stdout);
println!("{}", &labeled_stdout);

let balance = get(&labeled_stdout, "balance").unwrap();
Expand All @@ -57,6 +57,10 @@ fn milestone_3() {
.position(|c| c.is_ascii_whitespace())
.unwrap_or_else(|| gasprice.len());
assert_eq!(100000000, u64::from_str_radix(&gasprice[..i], 10).unwrap());

call(dir, &address, ["test_addmod()"]).unwrap();

call(dir, &address, ["test_mulmod()"]).unwrap();
}

fn label(stdout: &str) -> String {
Expand Down