fn lower_special_assembly(ctx: &mut LoweringContext, instructions: &mut Vec<Instruction>) -> bool {
match ctx.function_name.as_str() {
"extsload" | "exttload" => {
lower_extsload_single(ctx, instructions)
|| lower_extsload_range(ctx, instructions)
|| lower_extsload_slots(ctx, instructions)
}
_ => false,
}
}
fn lower_extsload_single(ctx: &mut LoweringContext, instructions: &mut Vec<Instruction>) -> bool {
let slot_index = match ctx.param_index_map.get("slot").copied() {
Some(index) if ctx.param_index_map.len() == 1 => index,
_ => return false,
};
instructions.push(Instruction::LoadParameter(slot_index));
instructions.push(Instruction::LoadStorageDynamic);
instructions.push(Instruction::Return);
true
}
fn lower_extsload_range(ctx: &mut LoweringContext, instructions: &mut Vec<Instruction>) -> bool {
let start_index = match ctx.param_index_map.get("startSlot").copied() {
Some(index) => index,
None => return false,
};
let count_index = match ctx.param_index_map.get("nSlots").copied() {
Some(index) => index,
None => return false,
};
if ctx.param_index_map.len() != 2 {
return false;
}
let start_local = ctx.allocate_local("__extsload_start".to_string(), None);
instructions.push(Instruction::LoadParameter(start_index));
instructions.push(Instruction::StoreLocal(start_local));
let count_local = ctx.allocate_local("__extsload_count".to_string(), None);
instructions.push(Instruction::LoadParameter(count_index));
instructions.push(Instruction::StoreLocal(count_local));
let array_element_type = ValueType::ByteArray {
fixed_len: Some(32),
};
let array_value_type = ValueType::Array(Box::new(array_element_type.clone()));
let array_local = ctx.allocate_local(
"__extsload_array".to_string(),
Some(array_value_type.clone()),
);
instructions.push(Instruction::LoadLocal(count_local));
instructions.push(Instruction::NewArray {
element_type: array_element_type,
});
instructions.push(Instruction::StoreLocal(array_local));
let index_local = ctx.allocate_local("__extsload_index".to_string(), None);
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
BigInt::zero(),
)));
instructions.push(Instruction::StoreLocal(index_local));
let value_local = ctx.allocate_local("__extsload_value".to_string(), None);
let loop_label = ctx.next_label();
let end_label = ctx.next_label();
instructions.push(Instruction::Label(loop_label));
instructions.push(Instruction::LoadLocal(index_local));
instructions.push(Instruction::LoadLocal(count_local));
instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
instructions.push(Instruction::JumpIf { target: end_label });
instructions.push(Instruction::LoadLocal(start_local));
instructions.push(Instruction::LoadStorageDynamic);
instructions.push(Instruction::StoreLocal(value_local));
instructions.push(Instruction::LoadLocal(array_local));
instructions.push(Instruction::LoadLocal(index_local));
instructions.push(Instruction::LoadLocal(value_local));
instructions.push(Instruction::ArraySet);
instructions.push(Instruction::LoadLocal(index_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
BigInt::from(1u8),
)));
instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
instructions.push(Instruction::StoreLocal(index_local));
instructions.push(Instruction::LoadLocal(start_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
BigInt::from(1u8),
)));
instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
instructions.push(Instruction::StoreLocal(start_local));
instructions.push(Instruction::Jump { target: loop_label });
instructions.push(Instruction::Label(end_label));
instructions.push(Instruction::LoadLocal(array_local));
instructions.push(Instruction::Return);
true
}
fn lower_extsload_slots(ctx: &mut LoweringContext, instructions: &mut Vec<Instruction>) -> bool {
let slots_index = match ctx.param_index_map.get("slots").copied() {
Some(index) if ctx.param_index_map.len() == 1 => index,
_ => return false,
};
let slots_local = ctx.allocate_local("__extsload_slots".to_string(), None);
instructions.push(Instruction::LoadParameter(slots_index));
instructions.push(Instruction::StoreLocal(slots_local));
let count_local = ctx.allocate_local("__extsload_count".to_string(), None);
instructions.push(Instruction::LoadLocal(slots_local));
instructions.push(Instruction::GetSize);
instructions.push(Instruction::StoreLocal(count_local));
let slots_array_element = ValueType::ByteArray {
fixed_len: Some(32),
};
let slots_array_type = ValueType::Array(Box::new(slots_array_element.clone()));
let array_local = ctx.allocate_local(
"__extsload_array".to_string(),
Some(slots_array_type.clone()),
);
instructions.push(Instruction::LoadLocal(count_local));
instructions.push(Instruction::NewArray {
element_type: slots_array_element,
});
instructions.push(Instruction::StoreLocal(array_local));
let index_local = ctx.allocate_local("__extsload_index".to_string(), None);
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
BigInt::zero(),
)));
instructions.push(Instruction::StoreLocal(index_local));
let value_local = ctx.allocate_local("__extsload_value".to_string(), None);
let loop_label = ctx.next_label();
let end_label = ctx.next_label();
instructions.push(Instruction::Label(loop_label));
instructions.push(Instruction::LoadLocal(index_local));
instructions.push(Instruction::LoadLocal(count_local));
instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
instructions.push(Instruction::JumpIf { target: end_label });
instructions.push(Instruction::LoadLocal(slots_local));
instructions.push(Instruction::LoadLocal(index_local));
instructions.push(Instruction::ArrayGet);
instructions.push(Instruction::LoadStorageDynamic);
instructions.push(Instruction::StoreLocal(value_local));
instructions.push(Instruction::LoadLocal(array_local));
instructions.push(Instruction::LoadLocal(index_local));
instructions.push(Instruction::LoadLocal(value_local));
instructions.push(Instruction::ArraySet);
instructions.push(Instruction::LoadLocal(index_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
BigInt::from(1u8),
)));
instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
instructions.push(Instruction::StoreLocal(index_local));
instructions.push(Instruction::Jump { target: loop_label });
instructions.push(Instruction::Label(end_label));
instructions.push(Instruction::LoadLocal(array_local));
instructions.push(Instruction::Return);
true
}
fn lower_yul_block(
block: &solang_parser::pt::YulBlock,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let snapshot = instructions.len();
let mut state = YulLoweringState::new();
for stmt in &block.statements {
if !lower_yul_statement(stmt, &mut state, ctx, instructions) {
instructions.truncate(snapshot);
return false;
}
}
let needs_memory_init = state.memory_local.is_some();
let needs_transient_init = state.transient_allocated_here;
let needs_returndata_init = state.returndata_allocated_here;
if needs_memory_init || needs_transient_init || needs_returndata_init {
let mut init: Vec<Instruction> = Vec::new();
if let Some(mem_local) = state.memory_local {
init.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
YUL_MEMORY_BYTES,
))));
init.push(Instruction::NewBuffer);
init.push(Instruction::StoreLocal(mem_local));
}
if needs_transient_init {
let transient_local = state
.transient_local
.expect("transient_allocated_here ⇒ transient_local populated");
init.push(Instruction::NewMap);
init.push(Instruction::StoreLocal(transient_local));
}
if needs_returndata_init {
let rd_local = state
.returndata_local
.expect("returndata_allocated_here ⇒ returndata_local populated");
init.push(Instruction::PushLiteral(LiteralValue::ByteArray(Vec::new())));
init.push(Instruction::StoreLocal(rd_local));
}
let tail = instructions.split_off(snapshot);
instructions.extend(init);
instructions.extend(tail);
}
true
}
const YUL_MEMORY_BYTES: u64 = 256;
struct YulLoweringState {
memory_local: Option<usize>,
transient_local: Option<usize>,
transient_allocated_here: bool,
returndata_local: Option<usize>,
returndata_allocated_here: bool,
yul_locals: std::collections::HashMap<String, usize>,
}
impl YulLoweringState {
fn new() -> Self {
Self {
memory_local: None,
transient_local: None,
transient_allocated_here: false,
returndata_local: None,
returndata_allocated_here: false,
yul_locals: std::collections::HashMap::new(),
}
}
fn ensure_memory(&mut self, ctx: &mut LoweringContext) -> usize {
if let Some(slot) = self.memory_local {
return slot;
}
let slot = ctx.allocate_local("__yul_memory".to_string(), None);
self.memory_local = Some(slot);
slot
}
fn ensure_transient(&mut self, ctx: &mut LoweringContext) -> usize {
if let Some(slot) = self.transient_local {
return slot;
}
if let Some(existing) = ctx.resolve_local("__yul_transient") {
self.transient_local = Some(existing);
return existing;
}
let slot = ctx.allocate_local("__yul_transient".to_string(), None);
self.transient_local = Some(slot);
self.transient_allocated_here = true;
slot
}
fn ensure_returndata(&mut self, ctx: &mut LoweringContext) -> usize {
if let Some(slot) = self.returndata_local {
return slot;
}
if let Some(existing) = ctx.resolve_local("__yul_returndata") {
self.returndata_local = Some(existing);
return existing;
}
let slot = ctx.allocate_local("__yul_returndata".to_string(), None);
self.returndata_local = Some(slot);
self.returndata_allocated_here = true;
slot
}
}
fn lower_yul_statement(
stmt: &solang_parser::pt::YulStatement,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
use solang_parser::pt::YulStatement;
match stmt {
YulStatement::VariableDeclaration(_, idents, init) => {
if idents.len() != 1 {
return false;
}
let ident = &idents[0];
let name = ident.id.name.clone();
let slot = ctx.allocate_local(format!("__yul_var_{name}"), None);
state.yul_locals.insert(name, slot);
if let Some(expr) = init {
if !lower_yul_expression(expr, state, ctx, instructions) {
return false;
}
instructions.push(Instruction::StoreLocal(slot));
} else {
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
BigInt::zero(),
)));
instructions.push(Instruction::StoreLocal(slot));
}
true
}
YulStatement::Assign(_, targets, value) => {
if targets.len() != 1 {
return false;
}
let solang_parser::pt::YulExpression::Variable(ident) = &targets[0] else {
return false;
};
enum YulAssignTarget {
Local(usize),
Parameter(usize),
}
let target = if let Some(&slot) = state.yul_locals.get(&ident.name) {
YulAssignTarget::Local(slot)
} else if let Some(¶m_index) = ctx.param_index_map.get(&ident.name) {
YulAssignTarget::Parameter(param_index)
} else if let Some(slot) = ctx.resolve_local(&ident.name) {
YulAssignTarget::Local(slot)
} else {
return false;
};
if !lower_yul_expression(value, state, ctx, instructions) {
return false;
}
match target {
YulAssignTarget::Local(slot) => {
instructions.push(Instruction::StoreLocal(slot));
}
YulAssignTarget::Parameter(param_index) => {
instructions.push(Instruction::StoreParameter(param_index));
}
}
true
}
YulStatement::FunctionCall(call) => {
lower_yul_function_call_as_statement(call, state, ctx, instructions)
}
YulStatement::Block(inner) => {
for inner_stmt in &inner.statements {
if !lower_yul_statement(inner_stmt, state, ctx, instructions) {
return false;
}
}
true
}
YulStatement::If(_, cond, body) => {
let end_label = ctx.next_label();
if !lower_yul_expression(cond, state, ctx, instructions) {
return false;
}
instructions.push(Instruction::JumpIf { target: end_label });
for inner_stmt in &body.statements {
if !lower_yul_statement(inner_stmt, state, ctx, instructions) {
return false;
}
}
instructions.push(Instruction::Label(end_label));
true
}
YulStatement::For(for_stmt) => {
for init_stmt in &for_stmt.init_block.statements {
if !lower_yul_statement(init_stmt, state, ctx, instructions) {
return false;
}
}
let loop_start = ctx.next_label();
let post_label = ctx.next_label();
let loop_end = ctx.next_label();
instructions.push(Instruction::Label(loop_start));
if !lower_yul_expression(&for_stmt.condition, state, ctx, instructions) {
return false;
}
instructions.push(Instruction::JumpIf { target: loop_end });
ctx.push_loop(post_label, loop_end);
for body_stmt in &for_stmt.execution_block.statements {
if !lower_yul_statement(body_stmt, state, ctx, instructions) {
ctx.pop_loop();
return false;
}
}
ctx.pop_loop();
instructions.push(Instruction::Label(post_label));
for post_stmt in &for_stmt.post_block.statements {
if !lower_yul_statement(post_stmt, state, ctx, instructions) {
return false;
}
}
instructions.push(Instruction::Jump { target: loop_start });
instructions.push(Instruction::Label(loop_end));
true
}
YulStatement::Switch(switch_stmt) => {
if !lower_yul_expression(&switch_stmt.condition, state, ctx, instructions) {
return false;
}
let disc_label = ctx.next_label();
let disc_local =
ctx.allocate_local(format!("__yul_switch_disc_{disc_label}"), None);
instructions.push(Instruction::StoreLocal(disc_local));
let end_label = ctx.next_label();
for case_opt in &switch_stmt.cases {
let solang_parser::pt::YulSwitchOptions::Case(_, value_expr, body) = case_opt
else {
return false;
};
let next_case_label = ctx.next_label();
instructions.push(Instruction::LoadLocal(disc_local));
if !lower_yul_expression(value_expr, state, ctx, instructions) {
return false;
}
instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
instructions.push(Instruction::Convert {
target: ConvertTarget::Integer,
});
instructions.push(Instruction::JumpIf {
target: next_case_label,
});
for body_stmt in &body.statements {
if !lower_yul_statement(body_stmt, state, ctx, instructions) {
return false;
}
}
instructions.push(Instruction::Jump { target: end_label });
instructions.push(Instruction::Label(next_case_label));
}
if let Some(default_opt) = &switch_stmt.default {
let solang_parser::pt::YulSwitchOptions::Default(_, default_body) = default_opt
else {
return false;
};
for body_stmt in &default_body.statements {
if !lower_yul_statement(body_stmt, state, ctx, instructions) {
return false;
}
}
}
instructions.push(Instruction::Label(end_label));
true
}
YulStatement::Break(_) => {
if let Some(label) = ctx.break_target() {
instructions.push(Instruction::Jump { target: label });
true
} else {
false
}
}
YulStatement::Continue(_) => {
if let Some(label) = ctx.continue_target() {
instructions.push(Instruction::Jump { target: label });
true
} else {
false
}
}
_ => false,
}
}
fn lower_yul_function_call_as_statement(
call: &solang_parser::pt::YulFunctionCall,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let name = call.id.name.as_str();
match name {
"mstore" => {
if call.arguments.len() != 2 {
return false;
}
lower_yul_mstore(&call.arguments[0], &call.arguments[1], state, ctx, instructions)
}
"mstore8" => {
false
}
"tstore" => {
if call.arguments.len() != 2 {
return false;
}
lower_yul_tstore(&call.arguments[0], &call.arguments[1], state, ctx, instructions)
}
"return" => {
if call.arguments.len() != 2 {
return false;
}
lower_yul_return(&call.arguments[0], &call.arguments[1], state, ctx, instructions)
}
"returndatacopy" => {
if call.arguments.len() != 3 {
return false;
}
lower_yul_returndatacopy(
&call.arguments[0],
&call.arguments[1],
&call.arguments[2],
state,
ctx,
instructions,
)
}
_ => false,
}
}
fn lower_yul_expression(
expr: &solang_parser::pt::YulExpression,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
use solang_parser::pt::YulExpression;
match expr {
YulExpression::NumberLiteral(_, integer, _exp, _) => {
match integer.parse::<BigInt>() {
Ok(value) => {
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(value)));
true
}
Err(_) => false,
}
}
YulExpression::HexNumberLiteral(_, raw, _) => {
let digits = raw.trim_start_matches("0x").trim_start_matches("0X");
match BigInt::parse_bytes(digits.as_bytes(), 16) {
Some(value) => {
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(value)));
true
}
None => false,
}
}
YulExpression::BoolLiteral(_, value, _) => {
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
u8::from(*value),
))));
true
}
YulExpression::Variable(ident) => {
if let Some(&slot) = state.yul_locals.get(&ident.name) {
instructions.push(Instruction::LoadLocal(slot));
return true;
}
if let Some(¶m_index) = ctx.param_index_map.get(&ident.name) {
instructions.push(Instruction::LoadParameter(param_index));
return true;
}
if let Some(slot) = ctx.resolve_local(&ident.name) {
instructions.push(Instruction::LoadLocal(slot));
return true;
}
false
}
YulExpression::FunctionCall(call) => {
lower_yul_function_call_as_expression(call, state, ctx, instructions)
}
_ => false,
}
}
fn lower_yul_function_call_as_expression(
call: &solang_parser::pt::YulFunctionCall,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let name = call.id.name.as_str();
match name {
"mload" => {
if call.arguments.len() != 1 {
return false;
}
lower_yul_mload(&call.arguments[0], state, ctx, instructions)
}
"tload" => {
if call.arguments.len() != 1 {
return false;
}
lower_yul_tload(&call.arguments[0], state, ctx, instructions)
}
"returndatasize" => {
if !call.arguments.is_empty() {
return false;
}
let rd_local = state.ensure_returndata(ctx);
instructions.push(Instruction::LoadLocal(rd_local));
instructions.push(Instruction::GetSize);
true
}
"add" | "sub" | "mul" => {
if call.arguments.len() != 2 {
return false;
}
if !lower_yul_expression(&call.arguments[0], state, ctx, instructions) {
return false;
}
if !lower_yul_expression(&call.arguments[1], state, ctx, instructions) {
return false;
}
let op = match name {
"add" => BinaryOperator::Add,
"sub" => BinaryOperator::Sub,
"mul" => BinaryOperator::Mul,
_ => unreachable!(),
};
instructions.push(Instruction::BinaryOp(op));
true
}
"div" | "mod" => {
if call.arguments.len() != 2 {
return false;
}
if !lower_yul_expression(&call.arguments[0], state, ctx, instructions) {
return false;
}
if !lower_yul_expression(&call.arguments[1], state, ctx, instructions) {
return false;
}
let tmp = ctx.next_label();
let b_local = ctx.allocate_local(format!("__yul_div_b_{tmp}"), None);
let a_local = ctx.allocate_local(format!("__yul_div_a_{tmp}"), None);
instructions.push(Instruction::StoreLocal(b_local)); instructions.push(Instruction::StoreLocal(a_local)); let nonzero = ctx.next_label();
let done = ctx.next_label();
instructions.push(Instruction::LoadLocal(b_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
instructions.push(Instruction::JumpIf { target: nonzero });
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::Jump { target: done });
instructions.push(Instruction::Label(nonzero));
instructions.push(Instruction::LoadLocal(a_local));
instructions.push(Instruction::LoadLocal(b_local));
emit_u256_divmod_ir(ctx, instructions, name == "mod");
instructions.push(Instruction::Label(done));
true
}
"and" | "or" | "xor" => {
if call.arguments.len() != 2 {
return false;
}
if !lower_yul_expression(&call.arguments[0], state, ctx, instructions) {
return false;
}
if !lower_yul_expression(&call.arguments[1], state, ctx, instructions) {
return false;
}
let op = match name {
"and" => BinaryOperator::BitAnd,
"or" => BinaryOperator::BitOr,
"xor" => BinaryOperator::BitXor,
_ => unreachable!(),
};
instructions.push(Instruction::BinaryOp(op));
true
}
"shl" | "shr" => {
if call.arguments.len() != 2 {
return false;
}
if !lower_yul_expression(&call.arguments[1], state, ctx, instructions) {
return false;
}
if !lower_yul_expression(&call.arguments[0], state, ctx, instructions) {
return false;
}
if name == "shr" {
emit_u256_logical_shr_ir(ctx, instructions);
} else {
instructions.push(Instruction::BinaryOp(BinaryOperator::Shl));
}
true
}
"lt" | "gt" | "eq" => {
if call.arguments.len() != 2 {
return false;
}
if !lower_yul_expression(&call.arguments[0], state, ctx, instructions) {
return false;
}
if !lower_yul_expression(&call.arguments[1], state, ctx, instructions) {
return false;
}
match name {
"lt" => emit_u256_unsigned_compare(instructions, BinaryOperator::Lt),
"gt" => emit_u256_unsigned_compare(instructions, BinaryOperator::Gt),
_ => instructions.push(Instruction::BinaryOp(BinaryOperator::Eq)),
}
instructions.push(Instruction::Convert {
target: ConvertTarget::Integer,
});
true
}
"iszero" => {
if call.arguments.len() != 1 {
return false;
}
if !lower_yul_expression(&call.arguments[0], state, ctx, instructions) {
return false;
}
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::BinaryOp(BinaryOperator::Eq));
instructions.push(Instruction::Convert {
target: ConvertTarget::Integer,
});
true
}
"not" => {
if call.arguments.len() != 1 {
return false;
}
if !lower_yul_expression(&call.arguments[0], state, ctx, instructions) {
return false;
}
let max_u256: BigInt = (BigInt::one() << 256usize) - BigInt::one();
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(max_u256)));
instructions.push(Instruction::BinaryOp(BinaryOperator::BitXor));
true
}
_ => false,
}
}
fn lower_yul_mstore(
offset_expr: &solang_parser::pt::YulExpression,
value_expr: &solang_parser::pt::YulExpression,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let mem_local = state.ensure_memory(ctx);
if !lower_yul_expression(value_expr, state, ctx, instructions) {
return false;
}
let tmp_id = ctx.next_label();
let value_local = ctx.allocate_local(format!("__yul_mstore_value_{tmp_id}"), None);
instructions.push(Instruction::StoreLocal(value_local));
if !lower_yul_expression(offset_expr, state, ctx, instructions) {
return false;
}
let offset_local = ctx.allocate_local(format!("__yul_mstore_offset_{tmp_id}"), None);
instructions.push(Instruction::StoreLocal(offset_local));
instructions.push(Instruction::LoadLocal(value_local));
instructions.push(Instruction::Convert {
target: ConvertTarget::ByteArray,
});
let src_local = ctx.allocate_local(format!("__yul_mstore_src_{tmp_id}"), None);
instructions.push(Instruction::StoreLocal(src_local));
let scratch_local = ctx.allocate_local(format!("__yul_mstore_scratch_{tmp_id}"), None);
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(32u64))));
instructions.push(Instruction::NewBuffer);
instructions.push(Instruction::StoreLocal(scratch_local));
let size_local = ctx.allocate_local(format!("__yul_mstore_size_{tmp_id}"), None);
let count_local = ctx.allocate_local(format!("__yul_mstore_count_{tmp_id}"), None);
instructions.push(Instruction::LoadLocal(src_local));
instructions.push(Instruction::GetSize);
instructions.push(Instruction::StoreLocal(size_local));
let ge_label = ctx.next_label();
let end_label = ctx.next_label();
instructions.push(Instruction::LoadLocal(size_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(32u64))));
instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
instructions.push(Instruction::JumpIf { target: ge_label });
instructions.push(Instruction::LoadLocal(size_local));
instructions.push(Instruction::StoreLocal(count_local));
instructions.push(Instruction::Jump { target: end_label });
instructions.push(Instruction::Label(ge_label));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(32u64))));
instructions.push(Instruction::StoreLocal(count_local));
instructions.push(Instruction::Label(end_label));
instructions.push(Instruction::LoadLocal(scratch_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::LoadLocal(src_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::LoadLocal(count_local));
instructions.push(Instruction::MemCpy);
instructions.push(Instruction::LoadLocal(scratch_local));
instructions.push(Instruction::ReverseItems);
instructions.push(Instruction::LoadLocal(mem_local));
instructions.push(Instruction::LoadLocal(offset_local));
instructions.push(Instruction::LoadLocal(scratch_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(32u64))));
instructions.push(Instruction::MemCpy);
true
}
fn lower_yul_mload(
offset_expr: &solang_parser::pt::YulExpression,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let mem_local = state.ensure_memory(ctx);
instructions.push(Instruction::LoadLocal(mem_local));
if !lower_yul_expression(offset_expr, state, ctx, instructions) {
return false;
}
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(32u64))));
instructions.push(Instruction::Substr);
instructions.push(Instruction::Convert {
target: ConvertTarget::ByteArray,
});
instructions.push(Instruction::Dup);
instructions.push(Instruction::ReverseItems);
instructions.push(Instruction::Convert {
target: ConvertTarget::Integer,
});
true
}
fn lower_yul_return(
offset_expr: &solang_parser::pt::YulExpression,
length_expr: &solang_parser::pt::YulExpression,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let mem_local = state.ensure_memory(ctx);
instructions.push(Instruction::LoadLocal(mem_local));
if !lower_yul_expression(offset_expr, state, ctx, instructions) {
return false;
}
if !lower_yul_expression(length_expr, state, ctx, instructions) {
return false;
}
instructions.push(Instruction::Substr);
let return_types = ctx.return_types();
let want_integer = return_types.len() == 1
&& matches!(
return_types[0],
ValueType::Integer { .. } | ValueType::Boolean
);
if want_integer {
instructions.push(Instruction::Convert {
target: ConvertTarget::ByteArray,
});
instructions.push(Instruction::Dup);
instructions.push(Instruction::ReverseItems);
instructions.push(Instruction::Convert {
target: ConvertTarget::Integer,
});
}
instructions.push(Instruction::Return);
true
}
fn lower_yul_tstore(
slot_expr: &solang_parser::pt::YulExpression,
value_expr: &solang_parser::pt::YulExpression,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let transient_local = state.ensure_transient(ctx);
instructions.push(Instruction::LoadLocal(transient_local));
if !lower_yul_expression(slot_expr, state, ctx, instructions) {
return false;
}
if !lower_yul_expression(value_expr, state, ctx, instructions) {
return false;
}
instructions.push(Instruction::ArraySet); true
}
fn lower_yul_tload(
slot_expr: &solang_parser::pt::YulExpression,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let transient_local = state.ensure_transient(ctx);
let tmp_id = ctx.next_label();
let slot_local = ctx.allocate_local(format!("__yul_tload_slot_{tmp_id}"), None);
if !lower_yul_expression(slot_expr, state, ctx, instructions) {
return false;
}
instructions.push(Instruction::StoreLocal(slot_local));
let missing_label = ctx.next_label();
let end_label = ctx.next_label();
instructions.push(Instruction::LoadLocal(transient_local));
instructions.push(Instruction::LoadLocal(slot_local));
instructions.push(Instruction::HasKey);
instructions.push(Instruction::JumpIf {
target: missing_label,
});
instructions.push(Instruction::LoadLocal(transient_local));
instructions.push(Instruction::LoadLocal(slot_local));
instructions.push(Instruction::ArrayGet); instructions.push(Instruction::Jump { target: end_label });
instructions.push(Instruction::Label(missing_label));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::Label(end_label));
true
}
fn lower_yul_returndatacopy(
dst_expr: &solang_parser::pt::YulExpression,
src_expr: &solang_parser::pt::YulExpression,
len_expr: &solang_parser::pt::YulExpression,
state: &mut YulLoweringState,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
let mem_local = state.ensure_memory(ctx);
let rd_local = state.ensure_returndata(ctx);
let tmp_id = ctx.next_label();
if !lower_yul_expression(dst_expr, state, ctx, instructions) {
return false;
}
let dst_local = ctx.allocate_local(format!("__yul_rdc_dst_{tmp_id}"), None);
instructions.push(Instruction::StoreLocal(dst_local));
if !lower_yul_expression(src_expr, state, ctx, instructions) {
return false;
}
let src_local = ctx.allocate_local(format!("__yul_rdc_src_{tmp_id}"), None);
instructions.push(Instruction::StoreLocal(src_local));
if !lower_yul_expression(len_expr, state, ctx, instructions) {
return false;
}
let len_local = ctx.allocate_local(format!("__yul_rdc_len_{tmp_id}"), None);
instructions.push(Instruction::StoreLocal(len_local));
let skip_label = ctx.next_label(); let ok_label = ctx.next_label();
instructions.push(Instruction::LoadLocal(len_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::BinaryOp(BinaryOperator::Ne));
instructions.push(Instruction::JumpIf {
target: skip_label,
});
instructions.push(Instruction::LoadLocal(src_local));
instructions.push(Instruction::LoadLocal(len_local));
instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
instructions.push(Instruction::LoadLocal(rd_local));
instructions.push(Instruction::GetSize);
instructions.push(Instruction::BinaryOp(BinaryOperator::Le));
instructions.push(Instruction::JumpIf { target: ok_label });
instructions.push(Instruction::PushLiteral(LiteralValue::String(
b"returndata: read past returndatasize".to_vec(),
)));
instructions.push(Instruction::Throw);
instructions.push(Instruction::Label(ok_label));
instructions.push(Instruction::LoadLocal(mem_local));
instructions.push(Instruction::LoadLocal(dst_local));
instructions.push(Instruction::LoadLocal(rd_local));
instructions.push(Instruction::LoadLocal(src_local));
instructions.push(Instruction::LoadLocal(len_local));
instructions.push(Instruction::MemCpy);
instructions.push(Instruction::Label(skip_label));
true
}