Skip to content

Commit

Permalink
More intrinsics and const_eval (FuelLabs#4260)
Browse files Browse the repository at this point in the history
## Description
New intrinsics and constant evaluations supported by this PR can be seen
in the additions to
[const_inits](https://github.com/FuelLabs/sway/compare/vaivaswatha/const_eval_407?expand=1#diff-5886ff784ee10cb93be850eba9966c80d539bf6c08eaeb7d286183494690c610)
test.

 Closes FuelLabs#407.

## 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
vaivaswatha authored Mar 10, 2023
1 parent 6338236 commit b48cabd
Show file tree
Hide file tree
Showing 22 changed files with 396 additions and 156 deletions.
47 changes: 47 additions & 0 deletions docs/book/src/reference/compiler_intrinsics.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,24 @@ __eq<T>(lhs: T, rhs: T) -> bool

___

```sway
__gt<T>(lhs: T, rhs: T) -> bool
```

**Description:** Returns whether `lhs` is greater than `rhs`.

**Constraints:** `T` is `u8`, `u16`, `u32`, `u64`.
___

```sway
__lt<T>(lhs: T, rhs: T) -> bool
```

**Description:** Returns whether `lhs` is less than `rhs`.

**Constraints:** `T` is `u8`, `u16`, `u32`, `u64`.
___

```sway
__gtf<T>(index: u64, tx_field_id: u64) -> T
```
Expand Down Expand Up @@ -154,6 +172,35 @@ __div<T>(lhs: T, rhs: T) -> T

___

```sway
__and<T>(lhs: T, rhs: T) -> T
```

**Description:** Bitwise AND `lhs` and `rhs`.

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`.

___

```sway
or<T>(lhs: T, rhs: T) -> T
```

**Description:** Bitwise OR `lhs` and `rhs`.

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`.

___

```sway
__xor<T>(lhs: T, rhs: T) -> T
```

**Description:** Bitwise XOR `lhs` and `rhs`.

**Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`.
___

```sway
__revert(code: u64)
```
Expand Down
15 changes: 15 additions & 0 deletions sway-ast/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub enum Intrinsic {
SizeOfType,
SizeOfVal,
Eq,
Gt,
Lt,
Gtf,
AddrOf,
StateClear,
Expand All @@ -19,6 +21,9 @@ pub enum Intrinsic {
Sub,
Mul,
Div,
And,
Or,
Xor,
Revert,
PtrAdd,
PtrSub,
Expand All @@ -33,6 +38,8 @@ impl fmt::Display for Intrinsic {
Intrinsic::SizeOfType => "size_of",
Intrinsic::SizeOfVal => "size_of_val",
Intrinsic::Eq => "eq",
Intrinsic::Gt => "gt",
Intrinsic::Lt => "lt",
Intrinsic::Gtf => "gtf",
Intrinsic::AddrOf => "addr_of",
Intrinsic::StateClear => "state_clear",
Expand All @@ -45,6 +52,9 @@ impl fmt::Display for Intrinsic {
Intrinsic::Sub => "sub",
Intrinsic::Mul => "mul",
Intrinsic::Div => "div",
Intrinsic::And => "and",
Intrinsic::Or => "or",
Intrinsic::Xor => "xor",
Intrinsic::Revert => "revert",
Intrinsic::PtrAdd => "ptr_add",
Intrinsic::PtrSub => "ptr_sub",
Expand All @@ -63,6 +73,8 @@ impl Intrinsic {
"__size_of" => SizeOfType,
"__size_of_val" => SizeOfVal,
"__eq" => Eq,
"__gt" => Gt,
"__lt" => Lt,
"__gtf" => Gtf,
"__addr_of" => AddrOf,
"__state_clear" => StateClear,
Expand All @@ -75,6 +87,9 @@ impl Intrinsic {
"__sub" => Sub,
"__mul" => Mul,
"__div" => Div,
"__and" => And,
"__or" => Or,
"__xor" => Xor,
"__revert" => Revert,
"__ptr_add" => PtrAdd,
"__ptr_sub" => PtrSub,
Expand Down
23 changes: 21 additions & 2 deletions sway-core/src/asm_generation/fuel/fuel_asm_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,9 @@ impl<'ir> FuelAsmBuilder<'ir> {
BinaryOpKind::Sub => Either::Left(VirtualOp::SUB(res_reg.clone(), val1_reg, val2_reg)),
BinaryOpKind::Mul => Either::Left(VirtualOp::MUL(res_reg.clone(), val1_reg, val2_reg)),
BinaryOpKind::Div => Either::Left(VirtualOp::DIV(res_reg.clone(), val1_reg, val2_reg)),
BinaryOpKind::And => Either::Left(VirtualOp::AND(res_reg.clone(), val1_reg, val2_reg)),
BinaryOpKind::Or => Either::Left(VirtualOp::OR(res_reg.clone(), val1_reg, val2_reg)),
BinaryOpKind::Xor => Either::Left(VirtualOp::XOR(res_reg.clone(), val1_reg, val2_reg)),
};
self.cur_bytecode.push(Op {
opcode,
Expand Down Expand Up @@ -621,12 +624,28 @@ impl<'ir> FuelAsmBuilder<'ir> {
let lhs_reg = self.value_to_register(lhs_value);
let rhs_reg = self.value_to_register(rhs_value);
let res_reg = self.reg_seqr.next();
let comment = String::new();
let owning_span = self.md_mgr.val_to_span(self.context, *instr_val);
match pred {
Predicate::Equal => {
self.cur_bytecode.push(Op {
opcode: Either::Left(VirtualOp::EQ(res_reg.clone(), lhs_reg, rhs_reg)),
comment: String::new(),
owning_span: self.md_mgr.val_to_span(self.context, *instr_val),
comment,
owning_span,
});
}
Predicate::LessThan => {
self.cur_bytecode.push(Op {
opcode: Either::Left(VirtualOp::LT(res_reg.clone(), lhs_reg, rhs_reg)),
comment,
owning_span,
});
}
Predicate::GreaterThan => {
self.cur_bytecode.push(Op {
opcode: Either::Left(VirtualOp::GT(res_reg.clone(), lhs_reg, rhs_reg)),
comment,
owning_span,
});
}
}
Expand Down
143 changes: 139 additions & 4 deletions sway-core/src/ir_generation/const_eval.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
use std::ops::{BitAnd, BitOr, BitXor};

use crate::{
asm_generation::from_ir::ir_type_size_in_bytes,
decl_engine::DeclEngine,
engine_threading::*,
language::{ty, CallPath},
language::{
ty::{self, TyIntrinsicFunctionKind},
CallPath,
},
metadata::MetadataManager,
semantic_analysis::*,
TypeEngine,
};

use super::{convert::convert_literal_to_constant, function::FnCompiler, types::*};
use super::{
convert::{convert_literal_to_constant, convert_resolved_typeid},
function::FnCompiler,
types::*,
};

use sway_error::error::CompileError;
use sway_ir::{
Expand All @@ -16,7 +26,7 @@ use sway_ir::{
metadata::combine as md_combine,
module::Module,
value::Value,
Instruction,
Instruction, Type,
};
use sway_types::{ident::Ident, span::Spanned};
use sway_utils::mapped_stack::MappedStack;
Expand Down Expand Up @@ -446,8 +456,10 @@ fn const_eval_typed_expr(
ty::TyExpressionVariant::MatchExp { desugared, .. } => {
const_eval_typed_expr(lookup, known_consts, desugared)?
}
ty::TyExpressionVariant::IntrinsicFunction(kind) => {
const_eval_intrinsic(lookup, known_consts, kind)?
}
ty::TyExpressionVariant::ArrayIndex { .. }
| ty::TyExpressionVariant::IntrinsicFunction(_)
| ty::TyExpressionVariant::CodeBlock(_)
| ty::TyExpressionVariant::Reassignment(_)
| ty::TyExpressionVariant::StorageReassignment(_)
Expand All @@ -466,6 +478,129 @@ fn const_eval_typed_expr(
})
}

fn const_eval_intrinsic(
lookup: &mut LookupEnv,
known_consts: &mut MappedStack<Ident, Constant>,
intrinsic: &TyIntrinsicFunctionKind,
) -> Result<Option<Constant>, CompileError> {
let args = intrinsic
.arguments
.iter()
.filter_map(|arg| const_eval_typed_expr(lookup, known_consts, arg).transpose())
.collect::<Result<Vec<_>, CompileError>>()?;

if args.len() != intrinsic.arguments.len() {
// We couldn't const-eval all arguments.
return Ok(None);
}
match intrinsic.kind {
sway_ast::Intrinsic::Add
| sway_ast::Intrinsic::Sub
| sway_ast::Intrinsic::Mul
| sway_ast::Intrinsic::Div
| sway_ast::Intrinsic::And
| sway_ast::Intrinsic::Or
| sway_ast::Intrinsic::Xor => {
let ty = args[0].ty;
assert!(
args.len() == 2 && ty.is_uint(lookup.context) && ty.eq(lookup.context, &args[1].ty)
);
let (ConstantValue::Uint(arg1), ConstantValue::Uint(ref arg2)) = (&args[0].value, &args[1].value)
else {
panic!("Type checker allowed incorrect args to binary op");
};
// All arithmetic is done as if it were u64
let result = match intrinsic.kind {
sway_ast::Intrinsic::Add => arg1.checked_add(*arg2),
sway_ast::Intrinsic::Sub => arg1.checked_sub(*arg2),
sway_ast::Intrinsic::Mul => arg1.checked_mul(*arg2),
sway_ast::Intrinsic::Div => arg1.checked_div(*arg2),
sway_ast::Intrinsic::And => Some(arg1.bitand(arg2)),
sway_ast::Intrinsic::Or => Some(arg1.bitor(*arg2)),
sway_ast::Intrinsic::Xor => Some(arg1.bitxor(*arg2)),
_ => unreachable!(),
};
match result {
Some(sum) => Ok(Some(Constant {
ty,
value: ConstantValue::Uint(sum),
})),
None => Ok(None),
}
}
sway_ast::Intrinsic::SizeOfType => {
let targ = &intrinsic.type_arguments[0];
let ir_type = convert_resolved_typeid(
lookup.type_engine,
lookup.decl_engine,
lookup.context,
&targ.type_id,
&targ.span,
)?;
Ok(Some(Constant {
ty: Type::get_uint64(lookup.context),
value: ConstantValue::Uint(ir_type_size_in_bytes(lookup.context, &ir_type)),
}))
}
sway_ast::Intrinsic::SizeOfVal => {
let val = &intrinsic.arguments[0];
let type_id = val.return_type;
let ir_type = convert_resolved_typeid(
lookup.type_engine,
lookup.decl_engine,
lookup.context,
&type_id,
&val.span,
)?;
Ok(Some(Constant {
ty: Type::get_uint64(lookup.context),
value: ConstantValue::Uint(ir_type_size_in_bytes(lookup.context, &ir_type)),
}))
}
sway_ast::Intrinsic::Eq => {
assert!(args.len() == 2);
Ok(Some(Constant {
ty: Type::get_bool(lookup.context),
value: ConstantValue::Bool(args[0].eq(lookup.context, &args[1])),
}))
}
sway_ast::Intrinsic::Gt => {
let (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) = (&args[0].value, &args[1].value)
else {
unreachable!("Type checker allowed non integer value for GreaterThan")
};
Ok(Some(Constant {
ty: Type::get_bool(lookup.context),
value: ConstantValue::Bool(val1 > val2),
}))
}
sway_ast::Intrinsic::Lt => {
let (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) = (&args[0].value, &args[1].value)
else {
unreachable!("Type checker allowed non integer value for LessThan")
};
Ok(Some(Constant {
ty: Type::get_bool(lookup.context),
value: ConstantValue::Bool(val1 < val2),
}))
}
sway_ast::Intrinsic::AddrOf => Ok(None),
sway_ast::Intrinsic::PtrAdd => Ok(None),
sway_ast::Intrinsic::PtrSub => Ok(None),
sway_ast::Intrinsic::GetStorageKey
| sway_ast::Intrinsic::IsReferenceType
| sway_ast::Intrinsic::Gtf
| sway_ast::Intrinsic::StateClear
| sway_ast::Intrinsic::StateLoadWord
| sway_ast::Intrinsic::StateStoreWord
| sway_ast::Intrinsic::StateLoadQuad
| sway_ast::Intrinsic::StateStoreQuad
| sway_ast::Intrinsic::Log
| sway_ast::Intrinsic::Revert
| sway_ast::Intrinsic::Smo => Ok(None),
}
}

fn const_eval_typed_ast_node(
lookup: &mut LookupEnv,
known_consts: &mut MappedStack<Ident, Constant>,
Expand Down
21 changes: 18 additions & 3 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,15 +531,21 @@ impl<'eng> FnCompiler<'eng> {
.get_storage_key()
.add_metadatum(context, span_md_idx))
}
Intrinsic::Eq => {
Intrinsic::Eq | Intrinsic::Gt | Intrinsic::Lt => {
let lhs = &arguments[0];
let rhs = &arguments[1];
let lhs_value = self.compile_expression(context, md_mgr, lhs)?;
let rhs_value = self.compile_expression(context, md_mgr, rhs)?;
let pred = match kind {
Intrinsic::Eq => Predicate::Equal,
Intrinsic::Gt => Predicate::GreaterThan,
Intrinsic::Lt => Predicate::LessThan,
_ => unreachable!(),
};
Ok(self
.current_block
.ins(context)
.cmp(Predicate::Equal, lhs_value, rhs_value))
.cmp(pred, lhs_value, rhs_value))
}
Intrinsic::Gtf => {
// The index is just a Value
Expand Down Expand Up @@ -729,12 +735,21 @@ impl<'eng> FnCompiler<'eng> {
}
}
}
Intrinsic::Add | Intrinsic::Sub | Intrinsic::Mul | Intrinsic::Div => {
Intrinsic::Add
| Intrinsic::Sub
| Intrinsic::Mul
| Intrinsic::Div
| Intrinsic::And
| Intrinsic::Or
| Intrinsic::Xor => {
let op = match kind {
Intrinsic::Add => BinaryOpKind::Add,
Intrinsic::Sub => BinaryOpKind::Sub,
Intrinsic::Mul => BinaryOpKind::Mul,
Intrinsic::Div => BinaryOpKind::Div,
Intrinsic::And => BinaryOpKind::And,
Intrinsic::Or => BinaryOpKind::Or,
Intrinsic::Xor => BinaryOpKind::Xor,
_ => unreachable!(),
};
let lhs = &arguments[0];
Expand Down
Loading

0 comments on commit b48cabd

Please sign in to comment.