Skip to content

Commit

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

This PR is part of #4794. It
implements support for u256 add operators.

The IR generated is a vanilla `add` operator as any other integer.

```rust
script {
    entry fn main() -> u256, !1 {
        entry():
        v0 = const u256 0x0000000000000000000000000000000000000000000000000000000000000001, !2
        v1 = const u256 0x0000000000000000000000000000000000000000000000000000000000000002, !3
        v2 = call add_0(v0, v1), !4
        ret u256 v2
    }
}
```

This will then be transformed by `miscdemotion` pass to something like:

```rust
script {
    entry fn main() -> u256 {
         local u256 __wide_lhs
         local mut u256 __wide_result
         local u256 __wide_rhs
 
        entry():       
         v0 = get_local ptr u256, __wide_lhs, !0
         v1 = const u256 0x0000000000000000000000000000000000000000000000000000000000000001, !0
         store v1 to v0, !0
         v2 = get_local ptr u256, __wide_rhs, !0
         v3 = const u256 0x0000000000000000000000000000000000000000000000000000000000000002, !0
         store v3 to v2, !0
         v4 = get_local ptr u256, __wide_result, !0
         wide add v0, v2 to v4, !0
         v5 = load v4, !0
         ret u256 v5
    }
}
```

Mind the `wide add` here. It is a Fuel specific operation, giving space
to other targets to implement this differently.


## 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.
  • Loading branch information
xunilrj authored Aug 2, 2023
1 parent 3efc60e commit 390b945
Show file tree
Hide file tree
Showing 22 changed files with 492 additions and 16 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

42 changes: 41 additions & 1 deletion sway-core/src/asm_generation/fuel/fuel_asm_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use crate::{
},
ProgramKind,
},
asm_lang::{virtual_register::*, Label, Op, VirtualImmediate12, VirtualImmediate18, VirtualOp},
asm_lang::{
virtual_register::*, Label, Op, VirtualImmediate06, VirtualImmediate12, VirtualImmediate18,
VirtualOp,
},
decl_engine::DeclRefFunction,
metadata::MetadataManager,
};
Expand Down Expand Up @@ -245,6 +248,12 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> {
FuelVmInstruction::StateStoreWord { stored_val, key } => {
self.compile_state_store_word(instr_val, stored_val, key)
}
FuelVmInstruction::WideBinaryOp {
op,
arg1,
arg2,
result,
} => self.compile_wide_binary_op(instr_val, op, arg1, arg2, result),
},
Instruction::GetElemPtr {
base,
Expand Down Expand Up @@ -475,6 +484,37 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> {
Ok(())
}

fn compile_wide_binary_op(
&mut self,
instr_val: &Value,
op: &BinaryOpKind,
arg1: &Value,
arg2: &Value,
result: &Value,
) -> Result<(), CompileError> {
let result_reg = self.value_to_register(result)?;
let val1_reg = self.value_to_register(arg1)?;
let val2_reg = self.value_to_register(arg2)?;

let opcode = match op {
BinaryOpKind::Add => VirtualOp::WQOP(
result_reg,
val1_reg,
val2_reg,
VirtualImmediate06::wide_op(crate::asm_lang::WideOperations::Add, true),
),
_ => todo!(),
};

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_binary_op(
&mut self,
instr_val: &Value,
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/asm_generation/fuel/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> {

let ptr_ty = ptr.get_inner_type(self.context);
let var_size = match ptr_ty.get_content(self.context) {
TypeContent::Uint(256) => 4,
TypeContent::Unit
| TypeContent::Bool
| TypeContent::Uint(_)
Expand Down
11 changes: 11 additions & 0 deletions sway-core/src/asm_lang/allocated_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ pub(crate) enum AllocatedOpcode {
SUBI(AllocatedRegister, AllocatedRegister, VirtualImmediate12),
XOR(AllocatedRegister, AllocatedRegister, AllocatedRegister),
XORI(AllocatedRegister, AllocatedRegister, VirtualImmediate12),
WQOP(
AllocatedRegister,
AllocatedRegister,
AllocatedRegister,
VirtualImmediate06,
),

/* Conrol Flow Instructions */
JMP(AllocatedRegister),
Expand Down Expand Up @@ -243,6 +249,7 @@ impl AllocatedOpcode {
SUBI(r1, _r2, _i) => vec![r1],
XOR(r1, _r2, _r3) => vec![r1],
XORI(r1, _r2, _i) => vec![r1],
WQOP(_, _, _, _) => vec![],

/* Control Flow Instructions */
JMP(_r1) => vec![],
Expand Down Expand Up @@ -356,6 +363,7 @@ impl fmt::Display for AllocatedOpcode {
SUBI(a, b, c) => write!(fmtr, "subi {a} {b} {c}"),
XOR(a, b, c) => write!(fmtr, "xor {a} {b} {c}"),
XORI(a, b, c) => write!(fmtr, "xori {a} {b} {c}"),
WQOP(a, b, c, d) => write!(fmtr, "wqop {a} {b} {c} {d}"),

/* Control Flow Instructions */
JMP(a) => write!(fmtr, "jmp {a}"),
Expand Down Expand Up @@ -500,6 +508,9 @@ impl AllocatedOp {
SUBI(a, b, c) => op::SUBI::new(a.to_reg_id(), b.to_reg_id(), c.value.into()).into(),
XOR(a, b, c) => op::XOR::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id()).into(),
XORI(a, b, c) => op::XORI::new(a.to_reg_id(), b.to_reg_id(), c.value.into()).into(),
WQOP(a, b, c, d) => {
op::WQOP::new(a.to_reg_id(), b.to_reg_id(), c.to_reg_id(), d.value.into()).into()
}

/* Control Flow Instructions */
JMP(a) => op::JMP::new(a.to_reg_id()).into(),
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/asm_lang/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,7 @@ impl fmt::Display for VirtualOp {
SUBI(a, b, c) => write!(fmtr, "subi {a} {b} {c}"),
XOR(a, b, c) => write!(fmtr, "xor {a} {b} {c}"),
XORI(a, b, c) => write!(fmtr, "xori {a} {b} {c}"),
WQOP(a, b, c, d) => write!(fmtr, "wqop {a} {b} {c} {d}"),

/* Control Flow Instructions */
JMP(a) => write!(fmtr, "jmp {a}"),
Expand Down
13 changes: 13 additions & 0 deletions sway-core/src/asm_lang/virtual_immediate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ use sway_types::span::Span;
use std::convert::TryInto;
use std::fmt;

#[repr(u8)]
pub enum WideOperations {
Add = 0,
}

/// 6-bit immediate value type
#[derive(Clone, Debug)]
pub struct VirtualImmediate06 {
Expand Down Expand Up @@ -36,7 +41,14 @@ impl VirtualImmediate06 {
value: raw.try_into().unwrap_or_else(|_| panic!("{}", msg.into())),
}
}

pub fn wide_op(op: WideOperations, rhs_indirect: bool) -> VirtualImmediate06 {
VirtualImmediate06 {
value: (op as u8) | if rhs_indirect { 32u8 } else { 0 },
}
}
}

impl fmt::Display for VirtualImmediate06 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "i{}", self.value)
Expand Down Expand Up @@ -73,6 +85,7 @@ impl VirtualImmediate12 {
}
}
}

impl fmt::Display for VirtualImmediate12 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "i{}", self.value)
Expand Down
21 changes: 21 additions & 0 deletions sway-core/src/asm_lang/virtual_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ pub(crate) enum VirtualOp {
SUBI(VirtualRegister, VirtualRegister, VirtualImmediate12),
XOR(VirtualRegister, VirtualRegister, VirtualRegister),
XORI(VirtualRegister, VirtualRegister, VirtualImmediate12),
WQOP(
VirtualRegister,
VirtualRegister,
VirtualRegister,
VirtualImmediate06,
),

/* Control Flow Instructions */
JMP(VirtualRegister),
Expand Down Expand Up @@ -207,6 +213,7 @@ impl VirtualOp {
SUBI(r1, r2, _i) => vec![r1, r2],
XOR(r1, r2, r3) => vec![r1, r2, r3],
XORI(r1, r2, _i) => vec![r1, r2],
WQOP(r1, r2, r3, _) => vec![r1, r2, r3],

/* Control Flow Instructions */
JMP(r1) => vec![r1],
Expand Down Expand Up @@ -316,6 +323,7 @@ impl VirtualOp {
SUBI(_r1, r2, _i) => vec![r2],
XOR(_r1, r2, r3) => vec![r2, r3],
XORI(_r1, r2, _i) => vec![r2],
WQOP(r1, r2, r3, _) => vec![r1, r2, r3],

/* Control Flow Instructions */
JMP(r1) => vec![r1],
Expand Down Expand Up @@ -425,6 +433,7 @@ impl VirtualOp {
SUBI(r1, _r2, _i) => vec![r1],
XOR(r1, _r2, _r3) => vec![r1],
XORI(r1, _r2, _i) => vec![r1],
WQOP(_, _, _, _) => vec![],

/* Control Flow Instructions */
JMP(_r1) => vec![],
Expand Down Expand Up @@ -670,6 +679,12 @@ impl VirtualOp {
update_reg(reg_to_reg_map, r2),
i.clone(),
),
WQOP(r1, r2, r3, i) => Self::WQOP(
update_reg(reg_to_reg_map, r1),
update_reg(reg_to_reg_map, r2),
update_reg(reg_to_reg_map, r3),
i.clone(),
),

/* Control Flow Instructions */
JMP(r1) => Self::JMP(update_reg(reg_to_reg_map, r1)),
Expand Down Expand Up @@ -1088,6 +1103,12 @@ impl VirtualOp {
map_reg(&mapping, reg2),
imm.clone(),
),
WQOP(reg1, reg2, reg3, imm) => AllocatedOpcode::WQOP(
map_reg(&mapping, reg1),
map_reg(&mapping, reg2),
map_reg(&mapping, reg3),
imm.clone(),
),

/* Control Flow Instructions */
JMP(reg1) => AllocatedOpcode::JMP(map_reg(&mapping, reg1)),
Expand Down
1 change: 1 addition & 0 deletions sway-ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ downcast-rs = "1.2.0"
filecheck = "0.5"
generational-arena = "0.2"
peg = "0.7"
prettydiff = "0.6.4"
rustc-hash = "1.1.0"
sway-ir-macros = { version = "0.43.0", path = "sway-ir-macros" }
sway-types = { version = "0.43.0", path = "../sway-types" }
Expand Down
42 changes: 41 additions & 1 deletion sway-ir/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ pub enum FuelVmInstruction {
stored_val: Value,
key: Value,
},

WideBinaryOp {
op: BinaryOpKind,
arg1: Value,
arg2: Value,
result: Value,
},
}

/// Comparison operations.
Expand Down Expand Up @@ -282,6 +289,10 @@ impl Instruction {
Instruction::MemCopyBytes { .. }
| Instruction::MemCopyVal { .. }
| Instruction::Store { .. } => Some(Type::get_unit(context)),

Instruction::FuelVm(FuelVmInstruction::WideBinaryOp { result, .. }) => {
result.get_type(context)
}
}
}

Expand Down Expand Up @@ -385,6 +396,9 @@ impl Instruction {
vec![*stored_val, *key, *number_of_slots]
}
FuelVmInstruction::StateStoreWord { stored_val, key } => vec![*stored_val, *key],
FuelVmInstruction::WideBinaryOp {
arg1, arg2, result, ..
} => vec![*result, *arg1, *arg2],
},
}
}
Expand Down Expand Up @@ -531,6 +545,13 @@ impl Instruction {
replace(key);
replace(stored_val);
}
FuelVmInstruction::WideBinaryOp {
arg1, arg2, result, ..
} => {
replace(arg1);
replace(arg2);
replace(result);
}
},
}
}
Expand All @@ -550,7 +571,8 @@ impl Instruction {
| Instruction::MemCopyBytes { .. }
| Instruction::MemCopyVal { .. }
| Instruction::Store { .. }
| Instruction::Ret(..) => true,
| Instruction::Ret(..)
| Instruction::FuelVm(FuelVmInstruction::WideBinaryOp { .. }) => true,

Instruction::UnaryOp { .. }
| Instruction::BinaryOp { .. }
Expand Down Expand Up @@ -687,6 +709,24 @@ impl<'a, 'eng> InstructionInserter<'a, 'eng> {
make_instruction!(self, Instruction::UnaryOp { op, arg })
}

pub fn wide_binary_op(
self,
op: BinaryOpKind,
arg1: Value,
arg2: Value,
result: Value,
) -> Value {
make_instruction!(
self,
Instruction::FuelVm(FuelVmInstruction::WideBinaryOp {
op,
arg1,
arg2,
result
})
)
}

pub fn binary_op(self, op: BinaryOpKind, arg1: Value, arg2: Value) -> Value {
make_instruction!(self, Instruction::BinaryOp { op, arg1, arg2 })
}
Expand Down
13 changes: 11 additions & 2 deletions sway-ir/src/optimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ pub mod tests {
{body}
}}
!0 = \"a.sw\""
!0 = \"a.sw\"
"
),
&source_engine,
)
Expand All @@ -87,7 +88,15 @@ pub mod tests {
group.append_pass(pass);
}

let before = context.to_string();
let modified = pass_manager.run(&mut context, &group).unwrap();
let after = context.to_string();

// print diff to help debug
if std::env::args().any(|x| x == "--nocapture") {
println!("{}", prettydiff::diff_lines(&before, &after));
}

assert_eq!(expected.is_some(), modified);

let Some(expected) = expected else {
Expand All @@ -98,7 +107,7 @@ pub mod tests {
.to_string()
.lines()
.filter_map(|x| {
if x.contains(", !0") {
if x.contains(", !") {
Some(format!("{}\n", x.trim()))
} else {
None
Expand Down
4 changes: 3 additions & 1 deletion sway-ir/src/optimize/dce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ fn get_loaded_symbols(context: &Context, val: Value) -> Vec<Symbol> {
Instruction::Store { dst_val_ptr: _, .. } => vec![],
Instruction::FuelVm(FuelVmInstruction::Gtf { .. })
| Instruction::FuelVm(FuelVmInstruction::ReadRegister(_))
| Instruction::FuelVm(FuelVmInstruction::Revert(_)) => vec![],
| Instruction::FuelVm(FuelVmInstruction::Revert(_))
| Instruction::FuelVm(FuelVmInstruction::WideBinaryOp { .. }) => vec![],
}
}

Expand Down Expand Up @@ -168,6 +169,7 @@ fn get_stored_symbols(context: &Context, val: Value) -> Vec<Symbol> {
vec![]
}
FuelVmInstruction::StateStoreQuadWord { stored_val: _, .. } => vec![],
FuelVmInstruction::WideBinaryOp { .. } => vec![],
},
}
}
Expand Down
11 changes: 11 additions & 0 deletions sway-ir/src/optimize/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,17 @@ fn inline_instruction(
FuelVmInstruction::StateStoreWord { stored_val, key } => new_block
.ins(context)
.state_store_word(map_value(stored_val), map_value(key)),
FuelVmInstruction::WideBinaryOp {
op,
arg1,
arg2,
result,
} => new_block.ins(context).wide_binary_op(
op,
map_value(arg1),
map_value(arg2),
map_value(result),
),
},
Instruction::GetElemPtr {
base,
Expand Down
Loading

0 comments on commit 390b945

Please sign in to comment.