Skip to content

Commit

Permalink
u256 not operator (#4924)
Browse files Browse the repository at this point in the history
## Description

This PR is part of #4794. It
implements the `not` operator for u256 and allows values bigger than
`u64`.

To support that it implements `U256` inside `sway-types`. For now, it is
a bare minimum wrapper around `BigUint`. We may use fuel macro in the
future, which implements all necessary functions.

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

---------

Co-authored-by: Anton Trunov <[email protected]>
  • Loading branch information
xunilrj and anton-trunov authored Aug 8, 2023
1 parent f4b155f commit 974bc2f
Show file tree
Hide file tree
Showing 27 changed files with 412 additions and 74 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions forc-plugins/forc-client/src/util/encode.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::str::FromStr;

use anyhow::Context;
use fuel_abi_types::abi::full_program::FullTypeApplication;
use serde::{Deserialize, Deserializer, Serialize};
use sway_types::u256::U256;

/// A wrapper around fuels_core::types::Token, which enables serde de/serialization.
#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -87,10 +89,10 @@ impl Token {
let u64_val = value.parse::<u64>()?;
Ok(Token(fuels_core::types::Token::U64(u64_val)))
}
//TODO u256 limited to u64 value
Type::U256 => {
let u64_val = value.parse::<u64>()?;
Ok(Token(fuels_core::types::Token::U256(u64_val.into())))
let v = value.parse::<U256>().context("u256 literal out of range")?;
let bytes = v.to_be_bytes();
Ok(Token(fuels_core::types::Token::U256(bytes.into())))
}
Type::Bool => {
let bool_val = value.parse::<bool>()?;
Expand Down
8 changes: 1 addition & 7 deletions sway-core/src/asm_generation/fuel/data_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,7 @@ impl Entry {
ConstantValue::Undef | ConstantValue::Unit => Entry::new_word(0, size, name),
ConstantValue::Bool(b) => Entry::new_word(u64::from(*b), size, name),
ConstantValue::Uint(u) => Entry::new_word(*u, size, name),
ConstantValue::U256(u) => {
// TODO u256 limited to u64
let mut bytes = vec![0u8; 24];
bytes.extend(&u.to_be_bytes());
assert!(bytes.len() == 32);
Entry::new_byte_array(bytes.to_vec(), size, name)
}
ConstantValue::U256(u) => Entry::new_byte_array(u.to_be_bytes().to_vec(), size, name),
ConstantValue::B256(bs) => Entry::new_byte_array(bs.to_vec(), size, name),
ConstantValue::String(bs) => Entry::new_byte_array(bs.clone(), size, name),

Expand Down
33 changes: 33 additions & 0 deletions sway-core/src/asm_generation/fuel/fuel_asm_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> {
FuelVmInstruction::StateStoreWord { stored_val, key } => {
self.compile_state_store_word(instr_val, stored_val, key)
}

// Wide operations
FuelVmInstruction::WideUnaryOp { op, result, arg } => {
self.compile_wide_unary_op(instr_val, op, arg, result)
}
FuelVmInstruction::WideBinaryOp {
op,
result,
Expand Down Expand Up @@ -530,6 +535,34 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> {
Ok(())
}

fn compile_wide_unary_op(
&mut self,
instr_val: &Value,
op: &UnaryOpKind,
arg: &Value,
result: &Value,
) -> Result<(), CompileError> {
let result_reg = self.value_to_register(result)?;
let val1_reg = self.value_to_register(arg)?;

let opcode = match op {
UnaryOpKind::Not => VirtualOp::WQOP(
result_reg,
val1_reg,
VirtualRegister::Constant(ConstantRegister::Zero),
VirtualImmediate06::wide_op(crate::asm_lang::WideOperations::Not, false),
),
};

self.cur_bytecode.push(Op {
opcode: Either::Left(opcode),
comment: String::new(),
owning_span: self.md_mgr.val_to_span(self.context, *instr_val),
});

Ok(())
}

fn compile_wide_binary_op(
&mut self,
instr_val: &Value,
Expand Down
6 changes: 3 additions & 3 deletions sway-core/src/asm_generation/miden_vm/miden_vm_asm_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,11 +648,11 @@ impl<'ir, 'eng> MidenVMAsmBuilder<'ir, 'eng> {
/// Pushes a constant to the top of the stack
pub(crate) fn render_constant(&self, constant: &Constant) -> Vec<DirectOp> {
use sway_ir::ConstantValue::*;
match constant.value {
match &constant.value {
Undef => todo!(),
Unit => vec![DirectOp::push(MidenStackValue::Unit)],
Bool(b) => vec![DirectOp::push(b)],
Uint(x) => vec![DirectOp::push(x)],
Bool(b) => vec![DirectOp::push(*b)],
Uint(x) => vec![DirectOp::push(*x)],
U256(x) => todo!(),
B256(_) => todo!(),
String(_) => todo!(),
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/asm_lang/virtual_immediate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::fmt;
pub enum WideOperations {
Add = 0,
Sub = 1,
Not = 2,
}

#[repr(u8)]
Expand Down
4 changes: 2 additions & 2 deletions sway-core/src/ir_generation/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub(super) fn convert_literal_to_value(context: &mut Context, ast_literal: &Lite
Literal::U16(n) => Constant::get_uint(context, 64, *n as u64),
Literal::U32(n) => Constant::get_uint(context, 64, *n as u64),
Literal::U64(n) => Constant::get_uint(context, 64, *n),
Literal::U256(n) => Constant::get_uint(context, 256, *n),
Literal::U256(n) => Constant::get_uint256(context, n.clone()),
Literal::Numeric(n) => Constant::get_uint(context, 64, *n),
Literal::String(s) => Constant::get_string(context, s.as_str().as_bytes().to_vec()),
Literal::Boolean(b) => Constant::get_bool(context, *b),
Expand All @@ -43,7 +43,7 @@ pub(super) fn convert_literal_to_constant(
Literal::U16(n) => Constant::new_uint(context, 64, *n as u64),
Literal::U32(n) => Constant::new_uint(context, 64, *n as u64),
Literal::U64(n) => Constant::new_uint(context, 64, *n),
Literal::U256(n) => Constant::new_uint(context, 256, *n),
Literal::U256(n) => Constant::new_uint256(context, n.clone()),
Literal::Numeric(n) => Constant::new_uint(context, 64, *n),
Literal::String(s) => Constant::new_string(context, s.as_str().as_bytes().to_vec()),
Literal::Boolean(b) => Constant::new_bool(context, *b),
Expand Down
4 changes: 2 additions & 2 deletions sway-core/src/language/literal.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{type_system::*, Engines};

use sway_error::error::CompileError;
use sway_types::{integer_bits::IntegerBits, span};
use sway_types::{integer_bits::IntegerBits, span, u256::U256};

use std::{
fmt,
Expand All @@ -15,7 +15,7 @@ pub enum Literal {
U16(u16),
U32(u32),
U64(u64),
U256(u64),
U256(U256),
String(span::Span),
Numeric(u64),
Boolean(bool),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,9 @@ impl Pattern {
Literal::U16(x) => Pattern::U16(Range::from_single(x)),
Literal::U32(x) => Pattern::U32(Range::from_single(x)),
Literal::U64(x) => Pattern::U64(Range::from_single(x)),
Literal::U256(x) => Pattern::U64(Range::from_single(x)),
Literal::U256(x) => Pattern::U64(Range::from_single(
x.try_into().expect("pattern only works with 64 bits"),
)),
Literal::B256(x) => Pattern::B256(x),
Literal::Boolean(b) => Pattern::Boolean(b),
Literal::Numeric(x) => Pattern::Numeric(Range::from_single(x)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use sway_error::{
handler::{ErrorEmitted, Handler},
warning::{CompileWarning, Warning},
};
use sway_types::{integer_bits::IntegerBits, Ident, Named, Span, Spanned};
use sway_types::{integer_bits::IntegerBits, u256::U256, Ident, Named, Span, Spanned};

use rustc_hash::FxHashSet;

Expand Down Expand Up @@ -1836,18 +1836,8 @@ impl ty::TyExpression {
}),
new_type,
),
//TODO u256 limited to u64 literals
IntegerBits::V256 => (
num.to_string().parse().map(Literal::U256).map_err(|e| {
Literal::handle_parse_int_error(
engines,
e,
TypeInfo::UnsignedInteger(IntegerBits::V256),
span.clone(),
)
}),
new_type,
),
// Numerics are limited to u64 for now
IntegerBits::V256 => (Ok(Literal::U256(U256::from(num))), new_type),
},
TypeInfo::Numeric => (
num.to_string().parse().map(Literal::Numeric).map_err(|e| {
Expand Down
12 changes: 1 addition & 11 deletions sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2888,17 +2888,7 @@ fn literal_to_literal(
};
Literal::U64(value)
}
// TODO u256 are limited to u64 literals for the moment
LitIntType::U256 => {
let value = match u64::try_from(parsed) {
Ok(value) => value,
Err(..) => {
let error = ConvertParseTreeError::U64LiteralOutOfRange { span };
return Err(handler.emit_err(error.into()));
}
};
Literal::U256(value)
}
LitIntType::U256 => Literal::U256(parsed.into()),
LitIntType::I8 | LitIntType::I16 | LitIntType::I32 | LitIntType::I64 => {
let error = ConvertParseTreeError::SignedIntegersNotSupported { span };
return Err(handler.emit_err(error.into()));
Expand Down
18 changes: 16 additions & 2 deletions sway-ir/src/constant.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! [`Constant`] is a typed constant value.
use crate::{context::Context, irtype::Type, pretty::DebugWithContext, value::Value};
use sway_types::u256::U256;

/// A [`Type`] and constant value, including [`ConstantValue::Undef`] for uninitialized constants.
#[derive(Debug, Clone, DebugWithContext)]
Expand All @@ -16,7 +17,7 @@ pub enum ConstantValue {
Unit,
Bool(bool),
Uint(u64),
U256(u64), // TODO u256 limited to u64 values
U256(U256),
B256([u8; 32]),
String(Vec<u8>),
Array(Vec<Constant>),
Expand All @@ -38,16 +39,24 @@ impl Constant {
}
}

/// For numbers bigger than u64 see `new_uint256`.
pub fn new_uint(context: &mut Context, nbits: u16, n: u64) -> Self {
Constant {
ty: Type::new_uint(context, nbits),
value: match nbits {
256 => ConstantValue::U256(n),
256 => ConstantValue::U256(n.into()),
_ => ConstantValue::Uint(n),
},
}
}

pub fn new_uint256(context: &mut Context, n: U256) -> Self {
Constant {
ty: Type::new_uint(context, 256),
value: ConstantValue::U256(n),
}
}

pub fn new_b256(context: &Context, bytes: [u8; 32]) -> Self {
Constant {
ty: Type::get_b256(context),
Expand Down Expand Up @@ -98,6 +107,11 @@ impl Constant {
Value::new_constant(context, new_const)
}

pub fn get_uint256(context: &mut Context, value: U256) -> Value {
let new_const = Constant::new_uint256(context, value);
Value::new_constant(context, new_const)
}

pub fn get_b256(context: &mut Context, value: [u8; 32]) -> Value {
let new_const = Constant::new_b256(context, value);
Value::new_constant(context, new_const)
Expand Down
21 changes: 21 additions & 0 deletions sway-ir/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,11 @@ pub enum FuelVmInstruction {
stored_val: Value,
key: Value,
},
WideUnaryOp {
op: UnaryOpKind,
result: Value,
arg: Value,
},
WideBinaryOp {
op: BinaryOpKind,
result: Value,
Expand Down Expand Up @@ -302,6 +307,9 @@ impl Instruction {
| Instruction::Store { .. } => Some(Type::get_unit(context)),

// Wide Operations
Instruction::FuelVm(FuelVmInstruction::WideUnaryOp { result, .. }) => {
result.get_type(context)
}
Instruction::FuelVm(FuelVmInstruction::WideBinaryOp { result, .. }) => {
result.get_type(context)
}
Expand Down Expand Up @@ -414,6 +422,7 @@ impl Instruction {
vec![*stored_val, *key, *number_of_slots]
}
FuelVmInstruction::StateStoreWord { stored_val, key } => vec![*stored_val, *key],
FuelVmInstruction::WideUnaryOp { arg, result, .. } => vec![*result, *arg],
FuelVmInstruction::WideBinaryOp {
arg1, arg2, result, ..
} => vec![*result, *arg1, *arg2],
Expand Down Expand Up @@ -571,6 +580,10 @@ impl Instruction {
replace(key);
replace(stored_val);
}
FuelVmInstruction::WideUnaryOp { arg, result, .. } => {
replace(arg);
replace(result);
}
FuelVmInstruction::WideBinaryOp {
arg1, arg2, result, ..
} => {
Expand Down Expand Up @@ -614,6 +627,7 @@ impl Instruction {
| Instruction::MemCopyVal { .. }
| Instruction::Store { .. }
| Instruction::Ret(..)
| Instruction::FuelVm(FuelVmInstruction::WideUnaryOp { .. })
| Instruction::FuelVm(FuelVmInstruction::WideBinaryOp { .. })
| Instruction::FuelVm(FuelVmInstruction::WideCmpOp { .. })
| Instruction::FuelVm(FuelVmInstruction::WideModularOp { .. }) => true,
Expand Down Expand Up @@ -753,6 +767,13 @@ impl<'a, 'eng> InstructionInserter<'a, 'eng> {
make_instruction!(self, Instruction::UnaryOp { op, arg })
}

pub fn wide_unary_op(self, op: UnaryOpKind, arg: Value, result: Value) -> Value {
make_instruction!(
self,
Instruction::FuelVm(FuelVmInstruction::WideUnaryOp { op, arg, result })
)
}

pub fn wide_binary_op(
self,
op: BinaryOpKind,
Expand Down
4 changes: 4 additions & 0 deletions sway-ir/src/optimize/dce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ fn get_loaded_symbols(context: &Context, val: Value) -> Vec<Symbol> {
Instruction::FuelVm(FuelVmInstruction::Gtf { .. })
| Instruction::FuelVm(FuelVmInstruction::ReadRegister(_))
| Instruction::FuelVm(FuelVmInstruction::Revert(_)) => vec![],
Instruction::FuelVm(FuelVmInstruction::WideUnaryOp { arg, .. }) => {
get_symbols(context, *arg).to_vec()
}
Instruction::FuelVm(FuelVmInstruction::WideBinaryOp { arg1, arg2, .. })
| Instruction::FuelVm(FuelVmInstruction::WideCmpOp { arg1, arg2, .. }) => {
get_symbols(context, *arg1)
Expand Down Expand Up @@ -184,6 +187,7 @@ fn get_stored_symbols(context: &Context, val: Value) -> Vec<Symbol> {
vec![]
}
FuelVmInstruction::StateStoreQuadWord { stored_val: _, .. } => vec![],
FuelVmInstruction::WideUnaryOp { result, .. } => get_symbols(context, *result).to_vec(),
FuelVmInstruction::WideBinaryOp { result, .. } => {
get_symbols(context, *result).to_vec()
}
Expand Down
3 changes: 3 additions & 0 deletions sway-ir/src/optimize/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,9 @@ fn inline_instruction(
FuelVmInstruction::StateStoreWord { stored_val, key } => new_block
.ins(context)
.state_store_word(map_value(stored_val), map_value(key)),
FuelVmInstruction::WideUnaryOp { op, arg, result } => new_block
.ins(context)
.wide_unary_op(op, map_value(arg), map_value(result)),
FuelVmInstruction::WideBinaryOp {
op,
arg1,
Expand Down
Loading

0 comments on commit 974bc2f

Please sign in to comment.