Skip to content

Commit

Permalink
b256 using wide ops (FuelLabs#5102)
Browse files Browse the repository at this point in the history
## Description

This PR change `b256` bitwise operations implementation to use wide
operators.
It does not add any new functionality to `b256`.

Other changes are related to `clippy` complaining about some clones.

## 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: Joshua Batty <[email protected]>
  • Loading branch information
xunilrj and JoshuaBatty authored Sep 25, 2023
1 parent 8d8eda3 commit 9c30219
Show file tree
Hide file tree
Showing 25 changed files with 537 additions and 376 deletions.
2 changes: 1 addition & 1 deletion sway-core/src/asm_generation/fuel/data_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl Entry {
ConstantValue::Bool(b) => Entry::new_word(u64::from(*b), size, name),
ConstantValue::Uint(u) => Entry::new_word(*u, 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::B256(bs) => Entry::new_byte_array(bs.to_be_bytes().to_vec(), size, name),
ConstantValue::String(bs) => Entry::new_byte_array(bs.clone(), size, name),

ConstantValue::Array(els) | ConstantValue::Struct(els) => Entry::new_collection(
Expand Down
14 changes: 7 additions & 7 deletions sway-core/src/asm_generation/fuel/register_allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,11 +146,11 @@ impl RegisterPool {
/// This function finally returns `live_out` because it has all the liveness information needed.
/// `live_in` is computed because it is needed to compute `live_out` iteratively.
///
pub(crate) fn liveness_analysis(ops: &[Op]) -> Vec<FxHashSet<VirtualRegister>> {
pub(crate) fn liveness_analysis(ops: &[Op]) -> Vec<BTreeSet<VirtualRegister>> {
// Vectors representing maps that will reprsent the live_in and live_out tables. Each entry
// corresponds to an instruction in `ops`.
let mut live_in: Vec<FxHashSet<VirtualRegister>> = vec![FxHashSet::default(); ops.len()];
let mut live_out: Vec<FxHashSet<VirtualRegister>> = vec![FxHashSet::default(); ops.len()];
let mut live_out: Vec<BTreeSet<VirtualRegister>> = vec![BTreeSet::default(); ops.len()];
let mut label_to_index: HashMap<Label, usize> = HashMap::new();

// Keep track of an map between jump labels and op indices. Useful to compute op successors.
Expand Down Expand Up @@ -222,7 +222,7 @@ pub(crate) fn liveness_analysis(ops: &[Op]) -> Vec<FxHashSet<VirtualRegister>> {
///
pub(crate) fn create_interference_graph(
ops: &[Op],
live_out: &[FxHashSet<VirtualRegister>],
live_out: &[BTreeSet<VirtualRegister>],
) -> (InterferenceGraph, HashMap<VirtualRegister, NodeIndex>) {
let mut interference_graph = InterferenceGraph::with_capacity(0, 0);

Expand Down Expand Up @@ -293,17 +293,17 @@ pub(crate) fn create_interference_graph(
///
pub(crate) fn coalesce_registers(
ops: &[Op],
live_out: Vec<FxHashSet<VirtualRegister>>,
live_out: Vec<BTreeSet<VirtualRegister>>,
interference_graph: &mut InterferenceGraph,
reg_to_node_map: &mut HashMap<VirtualRegister, NodeIndex>,
) -> (Vec<Op>, Vec<FxHashSet<VirtualRegister>>) {
) -> (Vec<Op>, Vec<BTreeSet<VirtualRegister>>) {
// A map from the virtual registers that are removed to the virtual registers that they are
// replaced with during the coalescing process.
let mut reg_to_reg_map: HashMap<&VirtualRegister, &VirtualRegister> = HashMap::new();

// To hold the final *reduced* list of ops
let mut reduced_ops: Vec<Op> = Vec::with_capacity(ops.len());
let mut reduced_live_out: Vec<FxHashSet<VirtualRegister>> = Vec::with_capacity(live_out.len());
let mut reduced_live_out: Vec<BTreeSet<VirtualRegister>> = Vec::with_capacity(live_out.len());
assert!(ops.len() == live_out.len());

for (op_idx, op) in ops.iter().enumerate() {
Expand Down Expand Up @@ -488,7 +488,7 @@ fn compute_def_use_points(ops: &[Op]) -> FxHashMap<VirtualRegister, (Vec<usize>,
pub(crate) fn color_interference_graph(
interference_graph: &mut InterferenceGraph,
ops: &[Op],
live_out: &[FxHashSet<VirtualRegister>],
live_out: &[BTreeSet<VirtualRegister>],
) -> Result<Vec<NodeIndex>, FxHashSet<VirtualRegister>> {
let mut stack = Vec::with_capacity(interference_graph.node_count());
let mut on_stack = FxHashSet::default();
Expand Down
1 change: 0 additions & 1 deletion sway-core/src/asm_lang/virtual_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,6 @@ impl VirtualOp {
pub(crate) fn allocate_registers(&self, pool: &RegisterPool) -> AllocatedOpcode {
let virtual_registers = self.registers();
let register_allocation_result = virtual_registers
.clone()
.into_iter()
.map(|x| match x {
VirtualRegister::Constant(c) => (x, Some(AllocatedRegister::Constant(*c))),
Expand Down
126 changes: 100 additions & 26 deletions sway-core/src/ir_generation/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,14 +730,7 @@ fn const_eval_intrinsic(
assert!(args.len() == intrinsic.arguments.len());

match intrinsic.kind {
Intrinsic::Add
| Intrinsic::Sub
| Intrinsic::Mul
| Intrinsic::Div
| Intrinsic::And
| Intrinsic::Or
| Intrinsic::Xor
| Intrinsic::Mod => {
Intrinsic::Add | Intrinsic::Sub | Intrinsic::Mul | Intrinsic::Div | Intrinsic::Mod => {
let ty = args[0].ty;
assert!(args.len() == 2 && ty.eq(lookup.context, &args[1].ty));

Expand All @@ -750,17 +743,14 @@ fn const_eval_intrinsic(
Intrinsic::Sub => arg1.checked_sub(*arg2),
Intrinsic::Mul => arg1.checked_mul(*arg2),
Intrinsic::Div => arg1.checked_div(*arg2),
Intrinsic::And => Some(arg1.bitand(arg2)),
Intrinsic::Or => Some(arg1.bitor(*arg2)),
Intrinsic::Xor => Some(arg1.bitxor(*arg2)),
Intrinsic::Mod => arg1.checked_rem(*arg2),
_ => unreachable!(),
};

match result {
Some(sum) => Ok(Some(Constant {
Some(result) => Ok(Some(Constant {
ty,
value: ConstantValue::Uint(sum),
value: ConstantValue::Uint(result),
})),
None => Err(ConstEvalError::CannotBeEvaluatedToConst {
span: intrinsic.span.clone(),
Expand All @@ -773,10 +763,55 @@ fn const_eval_intrinsic(
Intrinsic::Sub => arg1.checked_sub(arg2),
Intrinsic::Mul => arg1.checked_mul(arg2),
Intrinsic::Div => arg1.checked_div(arg2),
Intrinsic::Mod => Some(arg1.rem(arg2)),
_ => unreachable!(),
};

match result {
Some(result) => Ok(Some(Constant {
ty,
value: ConstantValue::U256(result),
})),
None => Err(ConstEvalError::CannotBeEvaluatedToConst {
span: intrinsic.span.clone(),
}),
}
}
_ => {
panic!("Type checker allowed incorrect args to binary op");
}
}
}
Intrinsic::And | Intrinsic::Or | Intrinsic::Xor => {
let ty = args[0].ty;
assert!(args.len() == 2 && ty.eq(lookup.context, &args[1].ty));

use ConstantValue::*;
match (&args[0].value, &args[1].value) {
(Uint(arg1), Uint(ref arg2)) => {
// All arithmetic is done as if it were u64
let result = match intrinsic.kind {
Intrinsic::And => Some(arg1.bitand(arg2)),
Intrinsic::Or => Some(arg1.bitor(*arg2)),
Intrinsic::Xor => Some(arg1.bitxor(*arg2)),
_ => unreachable!(),
};

match result {
Some(sum) => Ok(Some(Constant {
ty,
value: ConstantValue::Uint(sum),
})),
None => Err(ConstEvalError::CannotBeEvaluatedToConst {
span: intrinsic.span.clone(),
}),
}
}
(U256(arg1), U256(arg2)) => {
let result = match intrinsic.kind {
Intrinsic::And => Some(arg1.bitand(arg2)),
Intrinsic::Or => Some(arg1.bitor(arg2)),
Intrinsic::Xor => Some(arg1.bitxor(arg2)),
Intrinsic::Mod => Some(arg1.rem(arg2)),
_ => unreachable!(),
};

Expand All @@ -790,14 +825,32 @@ fn const_eval_intrinsic(
}),
}
}
(B256(arg1), B256(arg2)) => {
let result = match intrinsic.kind {
Intrinsic::And => Some(arg1.bitand(arg2)),
Intrinsic::Or => Some(arg1.bitor(arg2)),
Intrinsic::Xor => Some(arg1.bitxor(arg2)),
_ => unreachable!(),
};

match result {
Some(result) => Ok(Some(Constant {
ty,
value: ConstantValue::B256(result),
})),
None => Err(ConstEvalError::CannotBeEvaluatedToConst {
span: intrinsic.span.clone(),
}),
}
}
_ => {
panic!("Type checker allowed incorrect args to binary op");
}
}
}
Intrinsic::Lsh | Intrinsic::Rsh => {
assert!(args.len() == 2);
assert!(args[0].ty.is_uint(lookup.context));
assert!(args[0].ty.is_uint(lookup.context) || args[0].ty.is_b256(lookup.context));
assert!(args[1].ty.is_uint64(lookup.context));

let ty = args[0].ty;
Expand Down Expand Up @@ -842,6 +895,23 @@ fn const_eval_intrinsic(
}),
}
}
(B256(arg1), Uint(ref arg2)) => {
let result = match intrinsic.kind {
Intrinsic::Lsh => arg1.checked_shl(arg2),
Intrinsic::Rsh => Some(arg1.shr(arg2)),
_ => unreachable!(),
};

match result {
Some(result) => Ok(Some(Constant {
ty,
value: ConstantValue::B256(result),
})),
None => Err(ConstEvalError::CannotBeEvaluatedToConst {
span: intrinsic.span.clone(),
}),
}
}
_ => {
panic!("Type checker allowed incorrect args to binary op");
}
Expand Down Expand Up @@ -976,33 +1046,37 @@ fn const_eval_intrinsic(
span: intrinsic.span.clone(),
}),
Intrinsic::Not => {
// `not` works only with uint/u256 at the moment
// `not` works only with uint/u256/b256 at the moment
// `bool` ops::Not implementation uses `__eq`.

assert!(args.len() == 1);
assert!(args[0].ty.is_uint(lookup.context));
assert!(args[0].ty.is_uint(lookup.context) || args[0].ty.is_b256(lookup.context));

let Some(arg) = args.into_iter().next() else {
unreachable!("Unexpected 'not' without any arguments");
};

match arg.value {
ConstantValue::Uint(v) => {
let v = match arg.ty.get_uint_width(lookup.context) {
Some(8) => !(v as u8) as u64,
Some(16) => !(v as u16) as u64,
Some(32) => !(v as u32) as u64,
Some(64) => !v,
ConstantValue::Uint(n) => {
let n = match arg.ty.get_uint_width(lookup.context) {
Some(8) => !(n as u8) as u64,
Some(16) => !(n as u16) as u64,
Some(32) => !(n as u32) as u64,
Some(64) => !n,
_ => unreachable!("Invalid unsigned integer width"),
};
Ok(Some(Constant {
ty: arg.ty,
value: ConstantValue::Uint(v),
value: ConstantValue::Uint(n),
}))
}
ConstantValue::U256(v) => Ok(Some(Constant {
ConstantValue::U256(n) => Ok(Some(Constant {
ty: arg.ty,
value: ConstantValue::U256(n.not()),
})),
ConstantValue::B256(v) => Ok(Some(Constant {
ty: arg.ty,
value: ConstantValue::U256(v.not()),
value: ConstantValue::B256(v.not()),
})),
_ => {
unreachable!("Type checker allowed non integer value for Not");
Expand Down
21 changes: 17 additions & 4 deletions sway-core/src/ir_generation/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,16 @@ pub fn serialize_to_storage_slots(
),
)]
}
ConstantValue::U256(n) if ty.is_uint_of(context, 256) => {
vec![StorageSlot::new(
get_storage_key(ix, indices),
Bytes32::new(n.to_be_bytes()),
)]
}
ConstantValue::B256(b) if ty.is_b256(context) => {
vec![StorageSlot::new(
get_storage_key(ix, indices),
Bytes32::new(*b),
Bytes32::new(b.to_be_bytes()),
)]
}
ConstantValue::Array(_a) if ty.is_array(context) => {
Expand Down Expand Up @@ -157,9 +163,16 @@ pub fn serialize_to_words(constant: &Constant, context: &Context, ty: &Type) ->
ConstantValue::Uint(n) if ty.is_uint(context) => {
vec![Bytes8::new(n.to_be_bytes())]
}
ConstantValue::B256(b) if ty.is_b256(context) => {
Vec::from_iter((0..4).map(|i| Bytes8::new(b[8 * i..8 * i + 8].try_into().unwrap())))
}
ConstantValue::U256(n) if ty.is_uint_of(context, 256) => n
.to_be_bytes()
.chunks_exact(8)
.map(|x| Bytes8::new(x.try_into().expect("unexpected array size")))
.collect(),
ConstantValue::B256(b) if ty.is_b256(context) => b
.to_be_bytes()
.chunks_exact(8)
.map(|x| Bytes8::new(x.try_into().expect("unexpected array size")))
.collect(),
ConstantValue::String(s) if ty.is_string_array(context) => {
// Turn the bytes into serialized words (Bytes8).
let mut s = s.clone();
Expand Down
Loading

0 comments on commit 9c30219

Please sign in to comment.