diff --git a/docs/book/src/reference/compiler_intrinsics.md b/docs/book/src/reference/compiler_intrinsics.md index f6019ba1240..46a352fc413 100644 --- a/docs/book/src/reference/compiler_intrinsics.md +++ b/docs/book/src/reference/compiler_intrinsics.md @@ -44,6 +44,24 @@ __eq(lhs: T, rhs: T) -> bool ___ +```sway +__gt(lhs: T, rhs: T) -> bool +``` + +**Description:** Returns whether `lhs` is greater than `rhs`. + +**Constraints:** `T` is `u8`, `u16`, `u32`, `u64`. +___ + +```sway +__lt(lhs: T, rhs: T) -> bool +``` + +**Description:** Returns whether `lhs` is less than `rhs`. + +**Constraints:** `T` is `u8`, `u16`, `u32`, `u64`. +___ + ```sway __gtf(index: u64, tx_field_id: u64) -> T ``` @@ -154,6 +172,35 @@ __div(lhs: T, rhs: T) -> T ___ +```sway +__and(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(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(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) ``` diff --git a/sway-ast/src/intrinsics.rs b/sway-ast/src/intrinsics.rs index fc63bf8fac4..ace542ccb81 100644 --- a/sway-ast/src/intrinsics.rs +++ b/sway-ast/src/intrinsics.rs @@ -7,6 +7,8 @@ pub enum Intrinsic { SizeOfType, SizeOfVal, Eq, + Gt, + Lt, Gtf, AddrOf, StateClear, @@ -19,6 +21,9 @@ pub enum Intrinsic { Sub, Mul, Div, + And, + Or, + Xor, Revert, PtrAdd, PtrSub, @@ -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", @@ -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", @@ -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, @@ -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, diff --git a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs index e2a4c4448e5..86028f4eff1 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -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, @@ -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, }); } } diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 753f1e2a4c9..81bc7359b74 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -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::{ @@ -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; @@ -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(_) @@ -466,6 +478,129 @@ fn const_eval_typed_expr( }) } +fn const_eval_intrinsic( + lookup: &mut LookupEnv, + known_consts: &mut MappedStack, + intrinsic: &TyIntrinsicFunctionKind, +) -> Result, CompileError> { + let args = intrinsic + .arguments + .iter() + .filter_map(|arg| const_eval_typed_expr(lookup, known_consts, arg).transpose()) + .collect::, 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, diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 4e42b0af66b..59f84e788af 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -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 @@ -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]; diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index c4a3ca40993..b372317c633 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -38,7 +38,9 @@ impl ty::TyIntrinsicFunctionKind { Intrinsic::GetStorageKey => { type_check_get_storage_key(ctx, kind, arguments, type_arguments, span) } - Intrinsic::Eq => type_check_eq(ctx, kind, arguments, span), + Intrinsic::Eq | Intrinsic::Gt | Intrinsic::Lt => { + type_check_cmp(ctx, kind, arguments, span) + } Intrinsic::Gtf => type_check_gtf(ctx, kind, arguments, type_arguments, span), Intrinsic::AddrOf => type_check_addr_of(ctx, kind, arguments, span), Intrinsic::StateClear => type_check_state_clear(ctx, kind, arguments, span), @@ -50,9 +52,13 @@ impl ty::TyIntrinsicFunctionKind { type_check_state_quad(ctx, kind, arguments, type_arguments, span) } Intrinsic::Log => type_check_log(ctx, kind, arguments, span), - Intrinsic::Add | Intrinsic::Sub | Intrinsic::Mul | Intrinsic::Div => { - type_check_binary_op(ctx, kind, arguments, type_arguments, span) - } + Intrinsic::Add + | Intrinsic::Sub + | Intrinsic::Mul + | Intrinsic::Div + | Intrinsic::And + | Intrinsic::Or + | Intrinsic::Xor => type_check_binary_op(ctx, kind, arguments, type_arguments, span), Intrinsic::Revert => type_check_revert(ctx, kind, arguments, type_arguments, span), Intrinsic::PtrAdd | Intrinsic::PtrSub => { type_check_ptr_ops(ctx, kind, arguments, type_arguments, span) @@ -271,7 +277,15 @@ fn type_check_get_storage_key( /// Signature: `__eq(lhs: T, rhs: T) -> bool` /// Description: Returns whether `lhs` and `rhs` are equal. /// Constraints: `T` is `bool`, `u8`, `u16`, `u32`, `u64`, or `raw_ptr`. -fn type_check_eq( +/// +/// Signature: `__gt(lhs: T, rhs: T) -> bool` +/// Description: Returns whether `lhs` > `rhs`. +/// Constraints: `T` is `u8`, `u16`, `u32`, `u64`. +/// +/// Signature: `__lt(lhs: T, rhs: T) -> bool` +/// Description: Returns whether `lhs` < `rhs`. +/// Constraints: `T` is `u8`, `u16`, `u32`, `u64`. +fn type_check_cmp( mut ctx: TypeCheckContext, kind: sway_ast::Intrinsic, arguments: Vec, @@ -313,10 +327,9 @@ fn type_check_eq( warnings, errors ); - let is_valid_arg_ty = matches!( - arg_ty, - TypeInfo::UnsignedInteger(_) | TypeInfo::Boolean | TypeInfo::RawUntypedPtr - ); + let is_valid_arg_ty = matches!(arg_ty, TypeInfo::UnsignedInteger(_)) + || (matches!(&kind, Intrinsic::Eq) + && matches!(arg_ty, TypeInfo::Boolean | TypeInfo::RawUntypedPtr)); if !is_valid_arg_ty { errors.push(CompileError::IntrinsicUnsupportedArgType { name: kind.to_string(), @@ -988,6 +1001,18 @@ fn type_check_log( /// Signature: `__div(lhs: T, rhs: T) -> T` /// Description: Divides `lhs` and `rhs` and returns the result. /// Constraints: `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`. +/// +/// Signature: `__and(lhs: T, rhs: T) -> T` +/// Description: Bitwise And of `lhs` and `rhs` and returns the result. +/// Constraints: `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`. +/// +/// Signature: `__or(lhs: T, rhs: T) -> T` +/// Description: Bitwise Or `lhs` and `rhs` and returns the result. +/// Constraints: `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`. +/// +/// Signature: `__xor(lhs: T, rhs: T) -> T` +/// Description: Bitwise Xor `lhs` and `rhs` and returns the result. +/// Constraints: `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`. fn type_check_binary_op( mut ctx: TypeCheckContext, kind: sway_ast::Intrinsic, diff --git a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs index bfcf1d26330..541357e273a 100644 --- a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs +++ b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs @@ -615,8 +615,10 @@ fn effects_of_intrinsic(intr: &sway_ast::Intrinsic) -> HashSet { StateClear | StateStoreWord | StateStoreQuad => HashSet::from([Effect::StorageWrite]), StateLoadWord | StateLoadQuad => HashSet::from([Effect::StorageRead]), Smo => HashSet::from([Effect::OutputMessage]), - Revert | IsReferenceType | SizeOfType | SizeOfVal | Eq | Gtf | AddrOf | Log | Add | Sub - | Mul | Div | PtrAdd | PtrSub | GetStorageKey => HashSet::new(), + Revert | IsReferenceType | SizeOfType | SizeOfVal | Eq | Gt | Lt | Gtf | AddrOf | Log + | Add | Sub | Mul | Div | And | Or | Xor | PtrAdd | PtrSub | GetStorageKey => { + HashSet::new() + } } } diff --git a/sway-ir/src/instruction.rs b/sway-ir/src/instruction.rs index e71acefb379..5b09098953c 100644 --- a/sway-ir/src/instruction.rs +++ b/sway-ir/src/instruction.rs @@ -177,11 +177,12 @@ pub enum FuelVmInstruction { }, } +/// Comparison operations. #[derive(Debug, Clone, Copy)] pub enum Predicate { - /// Equivalence. Equal, - // More soon. NotEqual, LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual. + LessThan, + GreaterThan, } #[derive(Debug, Clone, Copy)] @@ -190,6 +191,9 @@ pub enum BinaryOpKind { Sub, Mul, Div, + And, + Or, + Xor, } /// Special registers in the Fuel Virtual Machine. diff --git a/sway-ir/src/optimize/constants.rs b/sway-ir/src/optimize/constants.rs index 58178716508..4b5fcbc9c12 100644 --- a/sway-ir/src/optimize/constants.rs +++ b/sway-ir/src/optimize/constants.rs @@ -116,11 +116,21 @@ fn combine_cmp(context: &mut Context, function: &Function) -> bool { let val2 = val2.get_constant(context).unwrap(); match pred { Predicate::Equal => { - if val1.eq(context, val2) { - Some((inst_val, block, true)) - } else { - Some((inst_val, block, false)) - } + Some((inst_val, block, val1.eq(context, val2))) + } + Predicate::GreaterThan => { + let (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) = (&val1.value, &val2.value) + else { + unreachable!("Type checker allowed non integer value for GreaterThan") + }; + Some((inst_val, block, val1 > val2)) + } + Predicate::LessThan => { + let (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) = (&val1.value, &val2.value) + else { + unreachable!("Type checker allowed non integer value for LessThan") + }; + Some((inst_val, block, val1 < val2)) } } } diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index 4c099c44e51..f2b3de74b84 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -159,6 +159,9 @@ mod ir_builder { / "sub" _ { BinaryOpKind::Sub } / "mul" _ { BinaryOpKind::Mul } / "div" _ { BinaryOpKind::Div } + / "and" _ { BinaryOpKind::And } + / "or" _ { BinaryOpKind::Or } + / "xor" _ { BinaryOpKind::Xor } rule operation() -> IrAstOperation = op_addr_of() @@ -372,10 +375,10 @@ mod ir_builder { IrAstOperation::Store(val, dst) } - rule cmp_pred() -> String - = p:$("eq") _ { - p.to_string() - } + rule cmp_pred() -> Predicate + = "eq" _ { Predicate::Equal } + / "gt" _ { Predicate::GreaterThan } + / "lt" _ { Predicate::LessThan } rule reg_name() -> String = r:$("of" / "pc" / "ssp" / "sp" / "fp" / "hp" / "err" / "ggas" / "cgas" / "bal" / "is" / "ret" / "retl" / "flag") _ { @@ -676,7 +679,7 @@ mod ir_builder { Call(String, Vec), CastPtr(String, IrAstTy, u64), Cbr(String, String, Vec, String, Vec), - Cmp(String, String, String), + Cmp(Predicate, String, String), Const(IrAstTy, IrAstConst), ContractCall(IrAstTy, String, String, String, String, String), ExtractElement(String, IrAstTy, String), @@ -1116,13 +1119,10 @@ mod ir_builder { .collect(), ) .add_metadatum(context, opt_metadata), - IrAstOperation::Cmp(pred_str, lhs, rhs) => block + IrAstOperation::Cmp(pred, lhs, rhs) => block .ins(context) .cmp( - match pred_str.as_str() { - "eq" => Predicate::Equal, - _ => unreachable!("Bug in `cmp` predicate rule."), - }, + pred, *val_map.get(&lhs).unwrap(), *val_map.get(&rhs).unwrap(), ) diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index ae476a4af16..92d6fc6e17a 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -417,6 +417,9 @@ fn instruction_to_doc<'a>( BinaryOpKind::Sub => "sub", BinaryOpKind::Mul => "mul", BinaryOpKind::Div => "div", + BinaryOpKind::And => "and", + BinaryOpKind::Or => "or", + BinaryOpKind::Xor => "xor", }; maybe_constant_to_doc(context, md_namer, namer, arg1) .append(maybe_constant_to_doc(context, md_namer, namer, arg2)) @@ -483,6 +486,8 @@ fn instruction_to_doc<'a>( Instruction::Cmp(pred, lhs_value, rhs_value) => { let pred_str = match pred { Predicate::Equal => "eq", + Predicate::LessThan => "lt", + Predicate::GreaterThan => "gt", }; maybe_constant_to_doc(context, md_namer, namer, lhs_value) .append(maybe_constant_to_doc(context, md_namer, namer, rhs_value)) diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index a9d75d4b4c1..91dea7f643a 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -226,61 +226,37 @@ pub trait Ord { impl Ord for u64 { fn gt(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - gt r3 r1 r2; - r3: bool - } + __gt(self, other) } fn lt(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - lt r3 r1 r2; - r3: bool - } + __lt(self, other) } } impl Ord for u32 { fn gt(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - gt r3 r1 r2; - r3: bool - } + __gt(self, other) } fn lt(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - lt r3 r1 r2; - r3: bool - } + __lt(self, other) } } impl Ord for u16 { fn gt(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - gt r3 r1 r2; - r3: bool - } + __gt(self, other) } fn lt(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - lt r3 r1 r2; - r3: bool - } + __lt(self, other) } } impl Ord for u8 { fn gt(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - gt r3 r1 r2; - r3: bool - } + __gt(self, other) } fn lt(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - lt r3 r1 r2; - r3: bool - } + __lt(self, other) } } @@ -326,37 +302,25 @@ pub trait BitwiseAnd { impl BitwiseAnd for u64 { fn binary_and(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - and r3 r1 r2; - r3: u64 - } + __and(self, other) } } impl BitwiseAnd for u32 { fn binary_and(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - and r3 r1 r2; - r3: u32 - } + __and(self, other) } } impl BitwiseAnd for u16 { fn binary_and(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - and r3 r1 r2; - r3: u16 - } + __and(self, other) } } impl BitwiseAnd for u8 { fn binary_and(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - and r3 r1 r2; - r3: u8 - } + __and(self, other) } } @@ -366,37 +330,25 @@ pub trait BitwiseOr { impl BitwiseOr for u64 { fn binary_or(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - or r3 r1 r2; - r3: u64 - } + __or(self, other) } } impl BitwiseOr for u32 { fn binary_or(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - or r3 r1 r2; - r3: u32 - } + __or(self, other) } } impl BitwiseOr for u16 { fn binary_or(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - or r3 r1 r2; - r3: u16 - } + __or(self, other) } } impl BitwiseOr for u8 { fn binary_or(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - or r3 r1 r2; - r3: u8 - } + __or(self, other) } } @@ -406,37 +358,25 @@ pub trait BitwiseXor { impl BitwiseXor for u64 { fn binary_xor(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - xor r3 r1 r2; - r3: u64 - } + __xor(self, other) } } impl BitwiseXor for u32 { fn binary_xor(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - xor r3 r1 r2; - r3: u32 - } + __xor(self, other) } } impl BitwiseXor for u16 { fn binary_xor(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - xor r3 r1 r2; - r3: u16 - } + __xor(self, other) } } impl BitwiseXor for u8 { fn binary_xor(self, other: Self) -> Self { - asm(r1: self, r2: other, r3) { - xor r3 r1 r2; - r3: u8 - } + __xor(self, other) } } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.lock deleted file mode 100644 index 0853ec41278..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.lock +++ /dev/null @@ -1,8 +0,0 @@ -[[package]] -name = 'const_nonconst_init' -source = 'member' -dependencies = ['core'] - -[[package]] -name = 'core' -source = 'path+from-root-E57A3612ABF8CF11' diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.toml deleted file mode 100644 index 8c981ae7e1b..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.toml +++ /dev/null @@ -1,8 +0,0 @@ -[project] -authors = ["Fuel Labs "] -entry = "main.sw" -license = "Apache-2.0" -name = "const_nonconst_init" - -[dependencies] -core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/src/main.sw deleted file mode 100644 index 207339315f9..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/src/main.sw +++ /dev/null @@ -1,10 +0,0 @@ -script; - -fn bla(x: u64) -> u64 { - x + 1 -} - -fn main() -> u64 { - const X = bla(0); - X -} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/test.toml deleted file mode 100644 index e92e4b0b7ee..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/test.toml +++ /dev/null @@ -1,4 +0,0 @@ -category = "fail" - -# check: $()const X = bla(0); -# nextln: $()Could not evaluate initializer to a const declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/src/main.sw index 45059447327..76a2f1ce081 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/src/main.sw @@ -3,16 +3,18 @@ contract; use core::*; storage { - x: u64 = 5 + 5, + x: u64 = 18446744073709551615 + 1, y: u64 = 5 + 5, } abi Test { + #[storage(read, write)] fn foo(); } impl Test for Contract { - fn foo() { + #[storage(read, write)] + fn foo() { storage.x += 1; storage.y += 1; } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/test.toml index 590c5b515ca..20876ab2e3b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/test.toml @@ -1,9 +1,5 @@ category = "fail" -# check: x: u64 = 5 + 5 +# check: x: u64 = 18446744073709551615 + 1 # nextln: $()Could not evaluate initializer to a const declaration. # nextln: y: u64 = 5 + 5 - -# check: x: u64 = 5 + 5 -# nextln: y: u64 = 5 + 5 -# nextln: $()Could not evaluate initializer to a const declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/binop_intrinsics/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/binop_intrinsics/src/main.sw index b9dac5ca1a4..a0ddf90f6c7 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/binop_intrinsics/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/binop_intrinsics/src/main.sw @@ -8,6 +8,9 @@ fn main() -> u64 { assert(__sub(b, a) == 20); assert(__mul(a, b) == 44); assert(__div(b, a) == 11); + assert(__and(a, b) == 2); + assert(__or(a, b) == 22); + assert(__xor(a, b) == 20); let a: u16 = 22; let b: u16 = 44; @@ -15,6 +18,9 @@ fn main() -> u64 { assert(__sub(b, a) == 22); assert(__mul(a, b) == 968); assert(__div(b, a) == 2); + assert(__and(a, b) == 4); + assert(__or(a, b) == 62); + assert(__xor(a, b) == 58); let a: u32 = 22; let b: u32 = 44; @@ -22,6 +28,9 @@ fn main() -> u64 { assert(__sub(b, a) == 22); assert(__mul(a, b) == 968); assert(__div(b, a) == 2); + assert(__and(a, b) == 4); + assert(__or(a, b) == 62); + assert(__xor(a, b) == 58); let a: u64 = 22; let b: u64 = 44; @@ -29,7 +38,12 @@ fn main() -> u64 { assert(__sub(b, a) == 22); assert(__mul(a, b) == 968); assert(__div(b, a) == 2); + assert(__and(a, b) == 4); + assert(__or(a, b) == 62); + assert(__xor(a, b) == 58); + assert(__xor(15, (__or(8, __and(5, 11)))) == 6); + assert(__gt(2, 1) && __lt(1, 2)); 2 } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/binop_intrinsics/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/binop_intrinsics/test.toml index 94c41acc0b6..248a13d14ca 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/binop_intrinsics/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/binop_intrinsics/test.toml @@ -1,4 +1,4 @@ category = "run" expected_result = { action = "return", value = 2 } validate_abi = true -expected_warnings = 12 +expected_warnings = 21 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw index f12c84c5d1f..9c9187343fe 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw @@ -25,6 +25,18 @@ enum En1 { NoVal: (), } +const X_SIZE: u64 = 4; +const Y_SIZE: u64 = 2; +const XPY = ((X_SIZE + Y_SIZE - 1) * 2) / 5; +const EN0A = En1::Int(XPY); +const TRUEB: bool = X_SIZE == 4; +const FALSEB: bool = X_SIZE == Y_SIZE; +const TRUEB1: bool = X_SIZE > Y_SIZE; +const FALSEB1: bool = X_SIZE < Y_SIZE; + +const SO = __size_of::(); +const SOV = __size_of_val("hello"); + const EN1a = En1::Int(101); const EN1b = En1::Arr(ARR2); const EN1c = En1::NoVal; @@ -37,6 +49,18 @@ const INT1 = 1; const ZERO_B256 = 0x0000000000000000000000000000000000000000000000000000000000000000; const KEY = ZERO_B256; +const BAR: u32 = 6; +const FOO: u32 = 5; +const MASK: u32 = 11; +const MASK2: u32 = 8; +const MASK3: u32 = 15; +const FOO_MIDDLE: u32 = ((FOO & MASK) | MASK2) ^ MASK3; + +const CARR1 = [X_SIZE - Y_SIZE + 1; 4]; +// This doesn't work because const-eval happens after type-checking, +// and the type checker needs to know the size of the array. +// const CARR2 = [1; X_SIZE - Y_SIZE + 1]; + fn main() -> u64 { const int1 = 1; assert(int1 == INT1 && ZERO_B256 == KEY); @@ -53,8 +77,19 @@ fn main() -> u64 { const a1 = [1, 2, 3]; assert(a1[0] == ARR1[0] && a1[1] == ARR1[1] && a1[2] == ARR1[2]); assert(a1[0] == ARR2[0] && a1[1] == ARR2[1] && a1[2] == ARR2[2]); + assert( + CARR1[0] == X_SIZE - Y_SIZE + 1 && + CARR1[1] == X_SIZE - Y_SIZE + 1 && + CARR1[2] == X_SIZE - Y_SIZE + 1 && + CARR1[3] == X_SIZE - Y_SIZE + 1 + ); // enum + match EN0A { + En1::Int(i) => assert(i == 2), + En1::Arr(_) => assert(false), + En1::NoVal => assert(false), + } match EN1a { En1::Int(i) => assert(i == 101), En1::Arr(_) => assert(false), @@ -76,6 +111,12 @@ fn main() -> u64 { // Struct and enum field access. assert(ETH_ID0.value == ETH_ID0_VALUE); assert(TUP1_idx2 == TUP1.2); + assert(XPY == 2); + assert(SO == __size_of::()); + assert(SOV == __size_of_val("hello")); + assert(TRUEB != FALSEB); + assert(TRUEB1 != FALSEB1); + assert(FOO_MIDDLE == BAR); 1 } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw index eae08bfb2c3..6fd4202b478 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw @@ -2,7 +2,7 @@ script; use basic_storage_abi::{BasicStorage, Quad}; fn main() -> u64 { - let addr = abi(BasicStorage, 0x89d66ade6874264291ca6416a6e746ac568970c385e19297d0f576ff05463be5); + let addr = abi(BasicStorage, 0xab8a772238071362b952208cbca054869a45eda6107376fe8c50a44e8ca284f1); let key = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; let value = 4242;