Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Y-Nak committed May 19, 2022
1 parent f4a7005 commit bf7ab9d
Show file tree
Hide file tree
Showing 21 changed files with 296 additions and 81 deletions.
5 changes: 2 additions & 3 deletions crates/codegen/src/yul/isel/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,10 +530,9 @@ impl<'db, 'a> FuncLowerHelper<'db, 'a> {
match op {
BinOp::Add => self.ctx.runtime.safe_add(self.db, lhs, rhs, inst_result_ty),
BinOp::Sub => self.ctx.runtime.safe_sub(self.db, lhs, rhs, inst_result_ty),
BinOp::Mul => expression! {mul([lhs], [rhs])},
BinOp::Mul => self.ctx.runtime.safe_mul(self.db, lhs, rhs, inst_result_ty),
BinOp::Div => self.ctx.runtime.safe_div(self.db, lhs, rhs, inst_result_ty),
BinOp::Mod if is_signed => expression! {smod([lhs], [rhs])},
BinOp::Mod => expression! {mod([lhs], [rhs])},
BinOp::Mod => self.ctx.runtime.safe_mod(self.db, lhs, rhs, inst_result_ty),
BinOp::Pow => self.ctx.runtime.safe_pow(self.db, lhs, rhs, inst_result_ty),
BinOp::Shl => expression! {shl([rhs], [lhs])},
BinOp::Shr => expression! {shr([rhs], [lhs])},
Expand Down
38 changes: 38 additions & 0 deletions crates/codegen/src/yul/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,14 @@ pub trait RuntimeProvider {
ty: TypeId,
) -> yul::Expression;

fn safe_mul(
&mut self,
db: &dyn CodegenDb,
lhs: yul::Expression,
rhs: yul::Expression,
ty: TypeId,
) -> yul::Expression;

fn safe_div(
&mut self,
db: &dyn CodegenDb,
Expand All @@ -191,6 +199,14 @@ pub trait RuntimeProvider {
ty: TypeId,
) -> yul::Expression;

fn safe_mod(
&mut self,
db: &dyn CodegenDb,
lhs: yul::Expression,
rhs: yul::Expression,
ty: TypeId,
) -> yul::Expression;

fn safe_pow(
&mut self,
db: &dyn CodegenDb,
Expand Down Expand Up @@ -620,6 +636,17 @@ impl RuntimeProvider for DefaultRuntimeProvider {
safe_math::dispatch_safe_sub(self, db, lhs, rhs, ty)
}

fn safe_mul(
&mut self,
db: &dyn CodegenDb,
lhs: yul::Expression,
rhs: yul::Expression,
ty: TypeId,
) -> yul::Expression {
debug_assert!(ty.is_integral(db.upcast()));
safe_math::dispatch_safe_mul(self, db, lhs, rhs, ty)
}

fn safe_div(
&mut self,
db: &dyn CodegenDb,
Expand All @@ -631,6 +658,17 @@ impl RuntimeProvider for DefaultRuntimeProvider {
safe_math::dispatch_safe_div(self, db, lhs, rhs, ty)
}

fn safe_mod(
&mut self,
db: &dyn CodegenDb,
lhs: yul::Expression,
rhs: yul::Expression,
ty: TypeId,
) -> yul::Expression {
debug_assert!(ty.is_integral(db.upcast()));
safe_math::dispatch_safe_mod(self, db, lhs, rhs, ty)
}

fn safe_pow(
&mut self,
db: &dyn CodegenDb,
Expand Down
136 changes: 136 additions & 0 deletions crates/codegen/src/yul/runtime/safe_math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,32 @@ pub(super) fn dispatch_safe_sub(
}
}

pub(super) fn dispatch_safe_mul(
provider: &mut DefaultRuntimeProvider,
db: &dyn CodegenDb,
lhs: yul::Expression,
rhs: yul::Expression,
ty: TypeId,
) -> yul::Expression {
debug_assert!(ty.is_integral(db.upcast()));
let min_value = get_min_value(db, ty);
let max_value = get_max_value(db, ty);

if ty.is_signed(db.upcast()) {
let name = "$safe_mul_signed";
let args = vec![lhs, rhs, min_value, max_value];
provider.create_then_call(name, args, |provider| {
make_safe_mul_signed(provider, db, name)
})
} else {
let name = "$safe_mul_unsigned";
let args = vec![lhs, rhs, max_value];
provider.create_then_call(name, args, |provider| {
make_safe_mul_unsigned(provider, db, name)
})
}
}

pub(super) fn dispatch_safe_div(
provider: &mut DefaultRuntimeProvider,
db: &dyn CodegenDb,
Expand All @@ -83,6 +109,29 @@ pub(super) fn dispatch_safe_div(
}
}

pub(super) fn dispatch_safe_mod(
provider: &mut DefaultRuntimeProvider,
db: &dyn CodegenDb,
lhs: yul::Expression,
rhs: yul::Expression,
ty: TypeId,
) -> yul::Expression {
debug_assert!(ty.is_integral(db.upcast()));
if ty.is_signed(db.upcast()) {
let name = "$safe_mod_signed";
let args = vec![lhs, rhs];
provider.create_then_call(name, args, |provider| {
make_safe_mod_signed(provider, db, name)
})
} else {
let name = "$safe_mod_unsigned";
let args = vec![lhs, rhs];
provider.create_then_call(name, args, |provider| {
make_safe_mod_unsigned(provider, db, name)
})
}
}

pub(super) fn dispatch_safe_pow(
provider: &mut DefaultRuntimeProvider,
db: &dyn CodegenDb,
Expand Down Expand Up @@ -192,6 +241,55 @@ fn make_safe_sub_unsigned(
RuntimeFunction::from_statement(func)
}

fn make_safe_mul_signed(
provider: &mut DefaultRuntimeProvider,
db: &dyn CodegenDb,
func_name: &str,
) -> RuntimeFunction {
let func_name = YulVariable::new(func_name);
let lhs = YulVariable::new("$lhs");
let rhs = YulVariable::new("$rhs");
let min_value = YulVariable::new("$min_value");
let max_value = YulVariable::new("$max_value");
let ret = YulVariable::new("$ret");

let func = function_definition! {
function [func_name.ident()]([lhs.ident()], [rhs.ident()], [min_value.ident()], [max_value.ident()]) -> [ret.ident()] {
// overflow, if lhs > 0, rhs > 0 and lhs > (max_value / rhs)
(if (and((and((sgt([lhs.expr()], 0)), (sgt([rhs.expr()], 0)))), (gt([lhs.expr()], (div([max_value.expr()], [rhs.expr()])))))) { [revert_with_overflow(provider, db)] })
// underflow, if lhs > 0, rhs < 0 and rhs < (min_value / lhs)
(if (and((and((sgt([lhs.expr()], 0)), (slt([rhs.expr()], 0)))), (slt([rhs.expr()], (sdiv([min_value.expr()], [lhs.expr()])))))) { [revert_with_overflow(provider, db)] })
// underflow, if lhs < 0, rhs > 0 and lhs < (min_value / rhs)
(if (and((and((slt([lhs.expr()], 0)), (sgt([rhs.expr()], 0)))), (slt([lhs.expr()], (sdiv([min_value.expr()], [rhs.expr()])))))) { [revert_with_overflow(provider, db)] })
// overflow, if lhs < 0, rhs < 0 and lhs < (max_value / rhs)
(if (and((and((slt([lhs.expr()], 0)), (slt([rhs.expr()], 0)))), (slt([lhs.expr()], (sdiv([max_value.expr()], [rhs.expr()])))))) { [revert_with_overflow(provider, db)] })
([ret.ident()] := mul([lhs.expr()], [rhs.expr()]))
}
};
RuntimeFunction::from_statement(func)
}

fn make_safe_mul_unsigned(
provider: &mut DefaultRuntimeProvider,
db: &dyn CodegenDb,
func_name: &str,
) -> RuntimeFunction {
let func_name = YulVariable::new(func_name);
let lhs = YulVariable::new("$lhs");
let rhs = YulVariable::new("$rhs");
let max_value = YulVariable::new("$max_value");
let ret = YulVariable::new("$ret");

let func = function_definition! {
function [func_name.ident()]([lhs.ident()], [rhs.ident()], [max_value.ident()]) -> [ret.ident()] {
// overflow, if lhs != 0 and rhs > (max_value / lhs)
(if (and((iszero((iszero([lhs.expr()])))), (gt([rhs.expr()], (div([max_value.expr()], [lhs.expr()])))))) { [revert_with_overflow(provider ,db)] })
([ret.ident()] := mul([lhs.expr()], [rhs.expr()]))
}
};
RuntimeFunction::from_statement(func)
}

fn make_safe_div_signed(
provider: &mut DefaultRuntimeProvider,
db: &dyn CodegenDb,
Expand Down Expand Up @@ -232,6 +330,44 @@ fn make_safe_div_unsigned(
RuntimeFunction::from_statement(func)
}

fn make_safe_mod_signed(
provider: &mut DefaultRuntimeProvider,
db: &dyn CodegenDb,
func_name: &str,
) -> RuntimeFunction {
let func_name = YulVariable::new(func_name);
let lhs = YulVariable::new("$lhs");
let rhs = YulVariable::new("$rhs");
let ret = YulVariable::new("$ret");

let func = function_definition! {
function [func_name.ident()]([lhs.ident()], [rhs.ident()]) -> [ret.ident()] {
(if (iszero([rhs.expr()])) { [revert_with_zero_division(provider, db)] })
([ret.ident()] := smod([lhs.expr()], [rhs.expr()]))
}
};
RuntimeFunction::from_statement(func)
}

fn make_safe_mod_unsigned(
provider: &mut DefaultRuntimeProvider,
db: &dyn CodegenDb,
func_name: &str,
) -> RuntimeFunction {
let func_name = YulVariable::new(func_name);
let lhs = YulVariable::new("$lhs");
let rhs = YulVariable::new("$rhs");
let ret = YulVariable::new("$ret");

let func = function_definition! {
function [func_name.ident()]([lhs.ident()], [rhs.ident()]) -> [ret.ident()] {
(if (iszero([rhs.expr()])) { [revert_with_zero_division(provider, db)] })
([ret.ident()] := mod([lhs.expr()], [rhs.expr()]))
}
};
RuntimeFunction::from_statement(func)
}

const SAFE_POW_HELPER_NAME: &str = "safe_pow_helper";

fn make_safe_pow_unsigned(
Expand Down
2 changes: 1 addition & 1 deletion crates/test-files/fixtures/demos/erc20_token.fe
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,4 @@ contract ERC20:
self._decimals = decimals_

fn _before_token_transfer(from: address, to: address, _ value: u256):
return
return
110 changes: 55 additions & 55 deletions crates/tests/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1157,61 +1157,61 @@ fn checked_arithmetic() {
Some(&int_token(-1)),
);

// // MULTIPLICATION
// // unsigned: max_value * 2 fails
// harness.test_function_reverts(
// &mut executor,
// &format!("mul_u{}", config.size),
// &[config.u_max.clone(), uint_token(2)],
// &encoded_over_or_underflow(),
// );

// // unsigned: max_value * 1 works
// harness.test_function(
// &mut executor,
// &format!("mul_u{}", config.size),
// &[config.u_max.clone(), uint_token(1)],
// Some(&config.u_max),
// );

// // signed: max_value * 2 fails
// harness.test_function_reverts(
// &mut executor,
// &format!("mul_i{}", config.size),
// &[config.i_max.clone(), int_token(2)],
// &encoded_over_or_underflow(),
// );

// // signed: max_value * 1 works
// harness.test_function(
// &mut executor,
// &format!("mul_i{}", config.size),
// &[config.i_max.clone(), int_token(1)],
// Some(&config.i_max),
// );

// // signed: max_value * -2 fails
// harness.test_function_reverts(
// &mut executor,
// &format!("mul_i{}", config.size),
// &[config.i_max.clone(), int_token(-2)],
// &encoded_over_or_underflow(),
// );

// // signed: min_value * -2 fails
// harness.test_function_reverts(
// &mut executor,
// &format!("mul_i{}", config.size),
// &[config.i_min.clone(), int_token(-2)],
// &encoded_over_or_underflow(),
// );

// harness.test_function(
// &mut executor,
// &format!("mul_i{}", config.size),
// &[config.i_min.clone(), int_token(1)],
// Some(&config.i_min),
// );
// MULTIPLICATION
// unsigned: max_value * 2 fails
harness.test_function_reverts(
&mut executor,
&format!("mul_u{}", config.size),
&[config.u_max.clone(), uint_token(2)],
&encoded_over_or_underflow(),
);

// unsigned: max_value * 1 works
harness.test_function(
&mut executor,
&format!("mul_u{}", config.size),
&[config.u_max.clone(), uint_token(1)],
Some(&config.u_max),
);

// signed: max_value * 2 fails
harness.test_function_reverts(
&mut executor,
&format!("mul_i{}", config.size),
&[config.i_max.clone(), int_token(2)],
&encoded_over_or_underflow(),
);

// signed: max_value * 1 works
harness.test_function(
&mut executor,
&format!("mul_i{}", config.size),
&[config.i_max.clone(), int_token(1)],
Some(&config.i_max),
);

// signed: max_value * -2 fails
harness.test_function_reverts(
&mut executor,
&format!("mul_i{}", config.size),
&[config.i_max.clone(), int_token(-2)],
&encoded_over_or_underflow(),
);

// signed: min_value * -2 fails
harness.test_function_reverts(
&mut executor,
&format!("mul_i{}", config.size),
&[config.i_min.clone(), int_token(-2)],
&encoded_over_or_underflow(),
);

harness.test_function(
&mut executor,
&format!("mul_i{}", config.size),
&[config.i_min.clone(), int_token(1)],
Some(&config.i_min),
);
}

assert_harness_gas_report!(harness);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
source: crates/tests/src/demo_uniswap.rs
expression: "format!(\"{}\", factory_harness.gas_reporter)"
---
create_pair([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643), Address(0x5f8bd49cd9f0cb2bd5bb9d4320dfe9b61023249d)]) used 1506155 gas
create_pair([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643), Address(0x5f8bd49cd9f0cb2bd5bb9d4320dfe9b61023249d)]) used 1549892 gas

Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ expression: "format!(\"{}\", pair_harness.gas_reporter)"
factory([]) used 275 gas
token0([]) used 297 gas
token1([]) used 319 gas
mint([Address(0x1000000000000000000000000000000000000001)]) used 148354 gas
mint([Address(0x1000000000000000000000000000000000000001)]) used 148429 gas
balanceOf([Address(0x1000000000000000000000000000000000000001)]) used 628 gas
balanceOf([Address(0x0000000000000000000000000000000000000000)]) used 628 gas
get_reserves([]) used 801 gas
swap([Uint(1993), Uint(0), Address(0x0000000000000000000000000000000000000042)]) used 35849 gas
swap([Uint(1993), Uint(0), Address(0x0000000000000000000000000000000000000042)]) used 36368 gas
get_reserves([]) used 801 gas
transfer([Address(0x62ac7b205016b92de2af65a252981f8e3394be7a), Uint(141421356237309503880)]) used 5802 gas
burn([Address(0x1000000000000000000000000000000000000001)]) used 3506 gas
transfer([Address(0xffb8f605926b517aff3e271e76904afb4b9999b2), Uint(141421356237309503880)]) used 5802 gas
burn([Address(0x1000000000000000000000000000000000000001)]) used 3650 gas
get_reserves([]) used 801 gas

Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
source: crates/tests/src/features.rs
expression: "format!(\"{}\", harness.gas_reporter)"
---
mul([Uint(10), Uint(42)]) used 274 gas
mul([Uint(10), Uint(42)]) used 340 gas

Loading

0 comments on commit bf7ab9d

Please sign in to comment.