fn lower_power_expression(
left: &Expression,
right: &Expression,
ctx: &mut LoweringContext,
instructions: &mut Vec<Instruction>,
) -> bool {
if let (Some(LiteralValue::Integer(base)), Some(LiteralValue::Integer(exp_lit))) = (
literal_from_expression(left),
literal_from_expression(right),
) {
if let Some(exp) = exp_lit.to_u32() {
const MAX_LITERAL_POW_EXP: u32 = 1024;
if exp <= MAX_LITERAL_POW_EXP {
let mut result = BigInt::one();
for _ in 0..exp {
result *= &base;
}
instructions
.push(Instruction::PushLiteral(LiteralValue::Integer(result)));
return true;
}
}
}
let base_local = ctx.allocate_local("__pow_base".to_string(), None);
let exp_local = ctx.allocate_local("__pow_exp".to_string(), None);
let result_local = ctx.allocate_local("__pow_result".to_string(), None);
if !lower_expression(left, ctx, instructions) {
return false;
}
instructions.push(Instruction::StoreLocal(base_local));
if !lower_expression(right, ctx, instructions) {
return false;
}
instructions.push(Instruction::StoreLocal(exp_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::one())));
instructions.push(Instruction::StoreLocal(result_local));
let loop_label = ctx.next_label();
let end_label = ctx.next_label();
let skip_mul_label = ctx.next_label();
instructions.push(Instruction::Label(loop_label));
instructions.push(Instruction::LoadLocal(exp_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::BinaryOp(BinaryOperator::Ne));
instructions.push(Instruction::JumpIf { target: end_label });
instructions.push(Instruction::LoadLocal(exp_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::one())));
instructions.push(Instruction::BinaryOp(BinaryOperator::BitAnd));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
instructions.push(Instruction::BinaryOp(BinaryOperator::Ne));
instructions.push(Instruction::JumpIf {
target: skip_mul_label,
});
instructions.push(Instruction::LoadLocal(result_local));
instructions.push(Instruction::LoadLocal(base_local));
instructions.push(Instruction::BinaryOp(BinaryOperator::Mul));
instructions.push(Instruction::StoreLocal(result_local));
instructions.push(Instruction::Label(skip_mul_label));
instructions.push(Instruction::LoadLocal(base_local));
instructions.push(Instruction::LoadLocal(base_local));
instructions.push(Instruction::BinaryOp(BinaryOperator::Mul));
instructions.push(Instruction::StoreLocal(base_local));
instructions.push(Instruction::LoadLocal(exp_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::one())));
instructions.push(Instruction::BinaryOp(BinaryOperator::Shr));
instructions.push(Instruction::StoreLocal(exp_local));
instructions.push(Instruction::Jump { target: loop_label });
instructions.push(Instruction::Label(end_label));
if let Some(ValueType::Integer { signed, bits }) = infer_type_from_expression(left, ctx) {
if bits == 256 && !signed {
if !ctx.in_unchecked_block() {
let two256 = BigInt::one() << 256usize;
let ok = ctx.next_label();
instructions.push(Instruction::LoadLocal(result_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(two256)));
instructions.push(Instruction::BinaryOp(BinaryOperator::Ge));
instructions.push(Instruction::JumpIf { target: ok });
emit_panic(0x11, instructions);
instructions.push(Instruction::Label(ok));
}
instructions.push(Instruction::LoadLocal(result_local));
emit_truncate_u256(instructions);
instructions.push(Instruction::StoreLocal(result_local));
} else if matches!(bits, 8 | 16 | 32 | 64 | 128) {
let bits_usize = bits as usize;
if ctx.in_unchecked_block() {
instructions.push(Instruction::LoadLocal(result_local));
if signed {
emit_truncate_narrow_signed(ctx, instructions, bits);
} else {
emit_truncate_narrow_unsigned(instructions, bits);
}
instructions.push(Instruction::StoreLocal(result_local));
} else if signed {
let int_max = (BigInt::one() << (bits_usize - 1)) - BigInt::one();
let int_min = -(BigInt::one() << (bits_usize - 1));
let after_max = ctx.next_label();
instructions.push(Instruction::LoadLocal(result_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(int_max)));
instructions.push(Instruction::BinaryOp(BinaryOperator::Gt));
instructions.push(Instruction::JumpIf { target: after_max });
emit_panic(0x11, instructions);
instructions.push(Instruction::Label(after_max));
let after_min = ctx.next_label();
instructions.push(Instruction::LoadLocal(result_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(int_min)));
instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
instructions.push(Instruction::JumpIf { target: after_min });
emit_panic(0x11, instructions);
instructions.push(Instruction::Label(after_min));
} else {
let uint_max = (BigInt::one() << bits_usize) - BigInt::one();
let after_max = ctx.next_label();
instructions.push(Instruction::LoadLocal(result_local));
instructions.push(Instruction::PushLiteral(LiteralValue::Integer(uint_max)));
instructions.push(Instruction::BinaryOp(BinaryOperator::Gt));
instructions.push(Instruction::JumpIf { target: after_max });
emit_panic(0x11, instructions);
instructions.push(Instruction::Label(after_max));
}
}
}
instructions.push(Instruction::LoadLocal(result_local));
true
}