From 5c0445de50373bafa92837d2eac4ebb7dfff40bc Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Thu, 8 Feb 2024 14:21:16 +0530 Subject: [PATCH 1/6] Support for a _jmp_to_ssp intrinsic Closes #5563 --- sway-ast/src/intrinsics.rs | 3 ++ .../asm_generation/fuel/fuel_asm_builder.rs | 15 +++++++ sway-core/src/ir_generation/const_eval.rs | 1 + sway-core/src/ir_generation/function.rs | 10 +++++ .../ty/expression/intrinsic_function.rs | 2 +- .../ast_node/expression/intrinsic_function.rs | 44 +++++++++++++++++++ .../semantic_analysis/cei_pattern_analysis.rs | 2 +- sway-ir/src/analysis/memory_utils.rs | 3 +- sway-ir/src/instruction.rs | 19 +++++++- sway-ir/src/optimize/fn_dedup.rs | 1 + sway-ir/src/optimize/inline.rs | 1 + sway-ir/src/printer.rs | 3 ++ sway-ir/src/verify.rs | 1 + 13 files changed, 100 insertions(+), 5 deletions(-) diff --git a/sway-ast/src/intrinsics.rs b/sway-ast/src/intrinsics.rs index ee6eb6e55e2..cc0723c56e2 100644 --- a/sway-ast/src/intrinsics.rs +++ b/sway-ast/src/intrinsics.rs @@ -35,6 +35,7 @@ pub enum Intrinsic { PtrSub, Smo, Not, + JmpToSsp, } impl fmt::Display for Intrinsic { @@ -73,6 +74,7 @@ impl fmt::Display for Intrinsic { Intrinsic::PtrSub => "ptr_sub", Intrinsic::Smo => "smo", Intrinsic::Not => "not", + Intrinsic::JmpToSsp => "jmp_to_ssp", }; write!(f, "{s}") } @@ -115,6 +117,7 @@ impl Intrinsic { "__ptr_sub" => PtrSub, "__smo" => Smo, "__not" => Not, + "__jmp_to_ssp" => JmpToSsp, _ => return None, }) } 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 2f9457174ce..52a161f6631 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -305,6 +305,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { arg2, arg3, } => self.compile_wide_modular_op(instr_val, op, result, arg1, arg2, arg3), + FuelVmInstruction::JmpToSsp => self.compile_jmp_to_ssp(instr_val), }, InstOp::GetElemPtr { base, @@ -1451,6 +1452,20 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { Ok(()) } + fn compile_jmp_to_ssp(&mut self, instr_val: &Value) -> Result<(), CompileError> { + let owning_span = self.md_mgr.val_to_span(self.context, *instr_val); + + self.cur_bytecode.push(Op { + owning_span, + opcode: Either::Left(VirtualOp::JMP(VirtualRegister::Constant( + ConstantRegister::StackStartPointer, + ))), + comment: "Jump to $ssp".into(), + }); + + Ok(()) + } + fn compile_smo( &mut self, instr_val: &Value, diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index fe4235f1932..7bafeb83d79 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -1096,6 +1096,7 @@ fn const_eval_intrinsic( | Intrinsic::StateStoreQuad | Intrinsic::Log | Intrinsic::Revert + | Intrinsic::JmpToSsp | Intrinsic::Smo => Err(ConstEvalError::CannotBeEvaluatedToConst { span: intrinsic.span.clone(), }), diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 7a5e137ad12..fe3ec2cbc62 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -1082,6 +1082,16 @@ impl<'eng> FnCompiler<'eng> { .add_metadatum(context, span_md_idx); Ok(TerminatorValue::new(val, context)) } + Intrinsic::JmpToSsp => { + // The `revert` instruction + let span_md_idx = md_mgr.span_to_md(context, &span); + let val = self + .current_block + .append(context) + .jmp_to_ssp() + .add_metadatum(context, span_md_idx); + Ok(TerminatorValue::new(val, context)) + } Intrinsic::PtrAdd | Intrinsic::PtrSub => { let op = match kind { Intrinsic::PtrAdd => BinaryOpKind::Add, diff --git a/sway-core/src/language/ty/expression/intrinsic_function.rs b/sway-core/src/language/ty/expression/intrinsic_function.rs index 6ead5f94fc1..eeeb2860b44 100644 --- a/sway-core/src/language/ty/expression/intrinsic_function.rs +++ b/sway-core/src/language/ty/expression/intrinsic_function.rs @@ -101,7 +101,7 @@ impl DebugWithEngines for TyIntrinsicFunctionKind { impl DeterministicallyAborts for TyIntrinsicFunctionKind { fn deterministically_aborts(&self, decl_engine: &DeclEngine, check_call_body: bool) -> bool { - matches!(self.kind, Intrinsic::Revert) + matches!(self.kind, Intrinsic::Revert | Intrinsic::JmpToSsp) || self .arguments .iter() 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 3643e05173d..442f28a4cf6 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 @@ -83,6 +83,9 @@ impl ty::TyIntrinsicFunctionKind { } Intrinsic::Smo => type_check_smo(handler, ctx, kind, arguments, type_arguments, span), Intrinsic::Not => type_check_not(handler, ctx, kind, arguments, type_arguments, span), + Intrinsic::JmpToSsp => { + type_check_jmp_to_ssp(handler, ctx, kind, arguments, type_arguments, span) + } } } } @@ -1171,6 +1174,47 @@ fn type_check_revert( )) } +/// Signature: `__jmp_to_ssp()` +/// Description: Jumps to $ssp. +/// Constraints: None. +fn type_check_jmp_to_ssp( + handler: &Handler, + ctx: TypeCheckContext, + kind: sway_ast::Intrinsic, + arguments: Vec, + type_arguments: Vec, + span: Span, +) -> Result<(ty::TyIntrinsicFunctionKind, TypeId), ErrorEmitted> { + let type_engine = ctx.engines.te(); + let engines = ctx.engines(); + + if !arguments.is_empty() { + return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 0, + span, + })); + } + + if !type_arguments.is_empty() { + return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumTArgs { + name: kind.to_string(), + expected: 0, + span, + })); + } + + Ok(( + ty::TyIntrinsicFunctionKind { + kind, + arguments: vec![], + type_arguments: vec![], + span, + }, + type_engine.insert(engines, TypeInfo::Tuple(vec![]), None), + )) +} + /// Signature: `__ptr_add(ptr: raw_ptr, offset: u64)` /// Description: Adds `offset` to the raw value of pointer `ptr`. /// Constraints: None. diff --git a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs index a27af3209ac..8217cae467f 100644 --- a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs +++ b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs @@ -608,7 +608,7 @@ 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 | IsStrArray | SizeOfType | SizeOfVal | SizeOfStr + Revert | JmpToSsp | IsReferenceType | IsStrArray | SizeOfType | SizeOfVal | SizeOfStr | AssertIsStrArray | ToStrArray | Eq | Gt | Lt | Gtf | AddrOf | Log | Add | Sub | Mul | Div | And | Or | Xor | Mod | Rsh | Lsh | PtrAdd | PtrSub | Not => HashSet::new(), } diff --git a/sway-ir/src/analysis/memory_utils.rs b/sway-ir/src/analysis/memory_utils.rs index 6f01c1e0005..af8db16a899 100644 --- a/sway-ir/src/analysis/memory_utils.rs +++ b/sway-ir/src/analysis/memory_utils.rs @@ -198,7 +198,7 @@ pub fn get_loaded_ptr_values(context: &Context, val: Value) -> Vec { InstOp::Store { dst_val_ptr: _, .. } => vec![], InstOp::FuelVm(FuelVmInstruction::Gtf { .. }) | InstOp::FuelVm(FuelVmInstruction::ReadRegister(_)) - | InstOp::FuelVm(FuelVmInstruction::Revert(_)) => vec![], + | InstOp::FuelVm(FuelVmInstruction::Revert(_) | FuelVmInstruction::JmpToSsp) => vec![], InstOp::FuelVm(FuelVmInstruction::WideUnaryOp { arg, .. }) => vec![*arg], InstOp::FuelVm(FuelVmInstruction::WideBinaryOp { arg1, arg2, .. }) | InstOp::FuelVm(FuelVmInstruction::WideCmpOp { arg1, arg2, .. }) => { @@ -249,6 +249,7 @@ pub fn get_stored_ptr_values(context: &Context, val: Value) -> Vec { | FuelVmInstruction::Log { .. } | FuelVmInstruction::ReadRegister(_) | FuelVmInstruction::Revert(_) + | FuelVmInstruction::JmpToSsp | FuelVmInstruction::Smo { .. } | FuelVmInstruction::StateClear { .. } => vec![], FuelVmInstruction::StateLoadQuadWord { load_val, .. } => vec![*load_val], diff --git a/sway-ir/src/instruction.rs b/sway-ir/src/instruction.rs index 0678f14c5f7..ff8a493a00b 100644 --- a/sway-ir/src/instruction.rs +++ b/sway-ir/src/instruction.rs @@ -194,6 +194,7 @@ pub enum FuelVmInstruction { arg1: Value, arg2: Value, }, + JmpToSsp, } /// Comparison operations. @@ -298,7 +299,7 @@ impl InstOp { // These are all terminators which don't return, essentially. No type. InstOp::Branch(_) | InstOp::ConditionalBranch { .. } - | InstOp::FuelVm(FuelVmInstruction::Revert(..)) + | InstOp::FuelVm(FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpToSsp) | InstOp::Ret(..) => None, // No-op is also no-type. @@ -408,6 +409,7 @@ impl InstOp { } => vec![*log_val, *log_id], FuelVmInstruction::ReadRegister(_) => vec![], FuelVmInstruction::Revert(v) => vec![*v], + FuelVmInstruction::JmpToSsp => vec![], FuelVmInstruction::Smo { recipient, message, @@ -544,6 +546,7 @@ impl InstOp { } FuelVmInstruction::ReadRegister { .. } => (), FuelVmInstruction::Revert(revert_val) => replace(revert_val), + FuelVmInstruction::JmpToSsp => (), FuelVmInstruction::Smo { recipient, message, @@ -629,7 +632,7 @@ impl InstOp { | InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord { .. }) | InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord { .. }) | InstOp::FuelVm(FuelVmInstruction::StateStoreWord { .. }) - | InstOp::FuelVm(FuelVmInstruction::Revert(..)) + | InstOp::FuelVm(FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpToSsp) | InstOp::MemCopyBytes { .. } | InstOp::MemCopyVal { .. } | InstOp::Store { .. } @@ -1072,6 +1075,18 @@ impl<'a, 'eng> InstructionInserter<'a, 'eng> { revert_val } + pub fn jmp_to_ssp(self) -> Value { + let jmp_to_ssp_val = Value::new_instruction( + self.context, + self.block, + InstOp::FuelVm(FuelVmInstruction::JmpToSsp), + ); + self.context.blocks[self.block.0] + .instructions + .push(jmp_to_ssp_val); + jmp_to_ssp_val + } + pub fn smo(self, recipient: Value, message: Value, message_size: Value, coins: Value) -> Value { insert_instruction!( self, diff --git a/sway-ir/src/optimize/fn_dedup.rs b/sway-ir/src/optimize/fn_dedup.rs index cbf737941e4..3756bf65680 100644 --- a/sway-ir/src/optimize/fn_dedup.rs +++ b/sway-ir/src/optimize/fn_dedup.rs @@ -154,6 +154,7 @@ fn hash_fn(context: &Context, function: Function, eq_class: &mut EqClass) -> u64 crate::FuelVmInstruction::Log { log_ty, .. } => log_ty.hash(state), crate::FuelVmInstruction::ReadRegister(reg) => reg.hash(state), crate::FuelVmInstruction::Revert(_) + | crate::FuelVmInstruction::JmpToSsp | crate::FuelVmInstruction::Smo { .. } | crate::FuelVmInstruction::StateClear { .. } | crate::FuelVmInstruction::StateLoadQuadWord { .. } diff --git a/sway-ir/src/optimize/inline.rs b/sway-ir/src/optimize/inline.rs index 89e3676c79c..bcf6daa9622 100644 --- a/sway-ir/src/optimize/inline.rs +++ b/sway-ir/src/optimize/inline.rs @@ -535,6 +535,7 @@ fn inline_instruction( new_block.append(context).read_register(reg) } FuelVmInstruction::Revert(val) => new_block.append(context).revert(map_value(val)), + FuelVmInstruction::JmpToSsp => new_block.append(context).jmp_to_ssp(), FuelVmInstruction::Smo { recipient, message, diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index 241765a89c6..ee9087398ca 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -656,6 +656,9 @@ fn instruction_to_doc<'a>( Doc::text(format!("revert {}", namer.name(context, v),)) .append(md_namer.md_idx_to_doc(context, metadata)), )), + FuelVmInstruction::JmpToSsp => Doc::line( + Doc::text("jmp_to_ssp").append(md_namer.md_idx_to_doc(context, metadata)), + ), FuelVmInstruction::Smo { recipient, message, diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index b8d876d4a0b..41bf3a15ccb 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -233,6 +233,7 @@ impl<'a, 'eng> InstructionVerifier<'a, 'eng> { log_id, } => self.verify_log(log_val, log_ty, log_id)?, FuelVmInstruction::ReadRegister(_) => (), + FuelVmInstruction::JmpToSsp => (), FuelVmInstruction::Revert(val) => self.verify_revert(val)?, FuelVmInstruction::Smo { recipient, From adc5da74865fdb23d8606ab65714d9267edf91e2 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Fri, 16 Feb 2024 10:41:47 +0530 Subject: [PATCH 2/6] Jump backwards from SSP by an offset --- sway-ast/src/intrinsics.rs | 6 +-- .../asm_generation/fuel/fuel_asm_builder.rs | 42 ++++++++++++++++--- sway-core/src/ir_generation/const_eval.rs | 2 +- sway-core/src/ir_generation/function.rs | 9 ++-- .../ty/expression/intrinsic_function.rs | 2 +- .../ast_node/expression/intrinsic_function.rs | 26 ++++++++---- .../semantic_analysis/cei_pattern_analysis.rs | 2 +- sway-ir/src/analysis/memory_utils.rs | 4 +- sway-ir/src/instruction.rs | 21 +++++----- sway-ir/src/optimize/fn_dedup.rs | 2 +- sway-ir/src/optimize/inline.rs | 4 +- sway-ir/src/printer.rs | 9 ++-- sway-ir/src/verify.rs | 2 +- 13 files changed, 88 insertions(+), 43 deletions(-) diff --git a/sway-ast/src/intrinsics.rs b/sway-ast/src/intrinsics.rs index cc0723c56e2..f2ca2817a63 100644 --- a/sway-ast/src/intrinsics.rs +++ b/sway-ast/src/intrinsics.rs @@ -35,7 +35,7 @@ pub enum Intrinsic { PtrSub, Smo, Not, - JmpToSsp, + JmpbSsp, } impl fmt::Display for Intrinsic { @@ -74,7 +74,7 @@ impl fmt::Display for Intrinsic { Intrinsic::PtrSub => "ptr_sub", Intrinsic::Smo => "smo", Intrinsic::Not => "not", - Intrinsic::JmpToSsp => "jmp_to_ssp", + Intrinsic::JmpbSsp => "jmpb_ssp", }; write!(f, "{s}") } @@ -117,7 +117,7 @@ impl Intrinsic { "__ptr_sub" => PtrSub, "__smo" => Smo, "__not" => Not, - "__jmp_to_ssp" => JmpToSsp, + "__jmpb_ssp" => JmpbSsp, _ => return None, }) } 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 52a161f6631..552618ada31 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -305,7 +305,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { arg2, arg3, } => self.compile_wide_modular_op(instr_val, op, result, arg1, arg2, arg3), - FuelVmInstruction::JmpToSsp => self.compile_jmp_to_ssp(instr_val), + FuelVmInstruction::JmpbSsp(offset) => self.compile_jmpb_ssp(instr_val, offset), }, InstOp::GetElemPtr { base, @@ -1452,15 +1452,45 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { Ok(()) } - fn compile_jmp_to_ssp(&mut self, instr_val: &Value) -> Result<(), CompileError> { + fn compile_jmpb_ssp(&mut self, instr_val: &Value, offset: &Value) -> Result<(), CompileError> { let owning_span = self.md_mgr.val_to_span(self.context, *instr_val); + let offset_reg = self.value_to_register(offset)?; + let is_offset_reg = self.reg_seqr.next(); + let prev_ssp_reg = self.reg_seqr.next(); + let by4_reg = self.reg_seqr.next(); + + self.cur_bytecode.push(Op { + owning_span: owning_span.clone(), + opcode: Either::Left(VirtualOp::SUB( + prev_ssp_reg.clone(), + VirtualRegister::Constant(ConstantRegister::StackStartPointer), + offset_reg, + )), + comment: "jmpb_ssp: Compute $ssp - offset".into(), + }); + self.cur_bytecode.push(Op { + owning_span: owning_span.clone(), + opcode: Either::Left(VirtualOp::SUB( + is_offset_reg.clone(), + prev_ssp_reg, + VirtualRegister::Constant(ConstantRegister::InstructionStart), + )), + comment: "jmpb_ssp: Subtract $is since $jmp adds it back.".into(), + }); + self.cur_bytecode.push(Op { + owning_span: owning_span.clone(), + opcode: Either::Left(VirtualOp::DIVI( + by4_reg.clone(), + is_offset_reg.clone(), + VirtualImmediate12::new(4, Span::dummy()).unwrap(), + )), + comment: "jmpb_ssp: Divide by 4 since Jmp multiplies by 4.".into(), + }); self.cur_bytecode.push(Op { owning_span, - opcode: Either::Left(VirtualOp::JMP(VirtualRegister::Constant( - ConstantRegister::StackStartPointer, - ))), - comment: "Jump to $ssp".into(), + opcode: Either::Left(VirtualOp::JMP(by4_reg)), + comment: "jmpb_ssp: Jump to computed value".into(), }); Ok(()) diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 7bafeb83d79..03185ab21ad 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -1096,7 +1096,7 @@ fn const_eval_intrinsic( | Intrinsic::StateStoreQuad | Intrinsic::Log | Intrinsic::Revert - | Intrinsic::JmpToSsp + | Intrinsic::JmpbSsp | Intrinsic::Smo => Err(ConstEvalError::CannotBeEvaluatedToConst { span: intrinsic.span.clone(), }), diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index fe3ec2cbc62..0bcbae604dc 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -1082,13 +1082,16 @@ impl<'eng> FnCompiler<'eng> { .add_metadatum(context, span_md_idx); Ok(TerminatorValue::new(val, context)) } - Intrinsic::JmpToSsp => { - // The `revert` instruction + Intrinsic::JmpbSsp => { + let contr_id_val = return_on_termination_or_extract!( + self.compile_expression_to_value(context, md_mgr, &arguments[0])? + ); + let span_md_idx = md_mgr.span_to_md(context, &span); let val = self .current_block .append(context) - .jmp_to_ssp() + .jmpb_ssp(contr_id_val) .add_metadatum(context, span_md_idx); Ok(TerminatorValue::new(val, context)) } diff --git a/sway-core/src/language/ty/expression/intrinsic_function.rs b/sway-core/src/language/ty/expression/intrinsic_function.rs index eeeb2860b44..eee447d8ec7 100644 --- a/sway-core/src/language/ty/expression/intrinsic_function.rs +++ b/sway-core/src/language/ty/expression/intrinsic_function.rs @@ -101,7 +101,7 @@ impl DebugWithEngines for TyIntrinsicFunctionKind { impl DeterministicallyAborts for TyIntrinsicFunctionKind { fn deterministically_aborts(&self, decl_engine: &DeclEngine, check_call_body: bool) -> bool { - matches!(self.kind, Intrinsic::Revert | Intrinsic::JmpToSsp) + matches!(self.kind, Intrinsic::Revert | Intrinsic::JmpbSsp) || self .arguments .iter() 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 442f28a4cf6..00fc4d7715c 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 @@ -83,8 +83,8 @@ impl ty::TyIntrinsicFunctionKind { } Intrinsic::Smo => type_check_smo(handler, ctx, kind, arguments, type_arguments, span), Intrinsic::Not => type_check_not(handler, ctx, kind, arguments, type_arguments, span), - Intrinsic::JmpToSsp => { - type_check_jmp_to_ssp(handler, ctx, kind, arguments, type_arguments, span) + Intrinsic::JmpbSsp => { + type_check_ldc_exec(handler, ctx, kind, arguments, type_arguments, span) } } } @@ -1174,12 +1174,12 @@ fn type_check_revert( )) } -/// Signature: `__jmp_to_ssp()` -/// Description: Jumps to $ssp. -/// Constraints: None. -fn type_check_jmp_to_ssp( +/// Signature: `__ldc_exec(contr_id: ContractId)` +/// Description: Loads contract and transfers control to it. +/// Constraints: contr_id has type `ContractId`. +fn type_check_ldc_exec( handler: &Handler, - ctx: TypeCheckContext, + mut ctx: TypeCheckContext, kind: sway_ast::Intrinsic, arguments: Vec, type_arguments: Vec, @@ -1188,7 +1188,7 @@ fn type_check_jmp_to_ssp( let type_engine = ctx.engines.te(); let engines = ctx.engines(); - if !arguments.is_empty() { + if arguments.len() != 1 { return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs { name: kind.to_string(), expected: 0, @@ -1204,10 +1204,18 @@ fn type_check_jmp_to_ssp( })); } + // Type check the argument which is the revert code + let mut ctx = ctx.by_ref().with_type_annotation(type_engine.insert( + engines, + TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), + None, + )); + let contr_id = ty::TyExpression::type_check(handler, ctx.by_ref(), arguments[0].clone())?; + Ok(( ty::TyIntrinsicFunctionKind { kind, - arguments: vec![], + arguments: vec![contr_id], type_arguments: vec![], span, }, diff --git a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs index 8217cae467f..037e0356ffc 100644 --- a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs +++ b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs @@ -608,7 +608,7 @@ 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 | JmpToSsp | IsReferenceType | IsStrArray | SizeOfType | SizeOfVal | SizeOfStr + Revert | JmpbSsp | IsReferenceType | IsStrArray | SizeOfType | SizeOfVal | SizeOfStr | AssertIsStrArray | ToStrArray | Eq | Gt | Lt | Gtf | AddrOf | Log | Add | Sub | Mul | Div | And | Or | Xor | Mod | Rsh | Lsh | PtrAdd | PtrSub | Not => HashSet::new(), } diff --git a/sway-ir/src/analysis/memory_utils.rs b/sway-ir/src/analysis/memory_utils.rs index af8db16a899..10a7c9fc6d3 100644 --- a/sway-ir/src/analysis/memory_utils.rs +++ b/sway-ir/src/analysis/memory_utils.rs @@ -198,7 +198,7 @@ pub fn get_loaded_ptr_values(context: &Context, val: Value) -> Vec { InstOp::Store { dst_val_ptr: _, .. } => vec![], InstOp::FuelVm(FuelVmInstruction::Gtf { .. }) | InstOp::FuelVm(FuelVmInstruction::ReadRegister(_)) - | InstOp::FuelVm(FuelVmInstruction::Revert(_) | FuelVmInstruction::JmpToSsp) => vec![], + | InstOp::FuelVm(FuelVmInstruction::Revert(_) | FuelVmInstruction::JmpbSsp(_)) => vec![], InstOp::FuelVm(FuelVmInstruction::WideUnaryOp { arg, .. }) => vec![*arg], InstOp::FuelVm(FuelVmInstruction::WideBinaryOp { arg1, arg2, .. }) | InstOp::FuelVm(FuelVmInstruction::WideCmpOp { arg1, arg2, .. }) => { @@ -249,7 +249,7 @@ pub fn get_stored_ptr_values(context: &Context, val: Value) -> Vec { | FuelVmInstruction::Log { .. } | FuelVmInstruction::ReadRegister(_) | FuelVmInstruction::Revert(_) - | FuelVmInstruction::JmpToSsp + | FuelVmInstruction::JmpbSsp(_) | FuelVmInstruction::Smo { .. } | FuelVmInstruction::StateClear { .. } => vec![], FuelVmInstruction::StateLoadQuadWord { load_val, .. } => vec![*load_val], diff --git a/sway-ir/src/instruction.rs b/sway-ir/src/instruction.rs index ff8a493a00b..f5179033069 100644 --- a/sway-ir/src/instruction.rs +++ b/sway-ir/src/instruction.rs @@ -194,7 +194,7 @@ pub enum FuelVmInstruction { arg1: Value, arg2: Value, }, - JmpToSsp, + JmpbSsp(Value), } /// Comparison operations. @@ -299,7 +299,7 @@ impl InstOp { // These are all terminators which don't return, essentially. No type. InstOp::Branch(_) | InstOp::ConditionalBranch { .. } - | InstOp::FuelVm(FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpToSsp) + | InstOp::FuelVm(FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpbSsp(..)) | InstOp::Ret(..) => None, // No-op is also no-type. @@ -408,8 +408,7 @@ impl InstOp { log_val, log_id, .. } => vec![*log_val, *log_id], FuelVmInstruction::ReadRegister(_) => vec![], - FuelVmInstruction::Revert(v) => vec![*v], - FuelVmInstruction::JmpToSsp => vec![], + FuelVmInstruction::Revert(v) | FuelVmInstruction::JmpbSsp(v) => vec![*v], FuelVmInstruction::Smo { recipient, message, @@ -546,7 +545,7 @@ impl InstOp { } FuelVmInstruction::ReadRegister { .. } => (), FuelVmInstruction::Revert(revert_val) => replace(revert_val), - FuelVmInstruction::JmpToSsp => (), + FuelVmInstruction::JmpbSsp(contr_id) => replace(contr_id), FuelVmInstruction::Smo { recipient, message, @@ -632,7 +631,7 @@ impl InstOp { | InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord { .. }) | InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord { .. }) | InstOp::FuelVm(FuelVmInstruction::StateStoreWord { .. }) - | InstOp::FuelVm(FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpToSsp) + | InstOp::FuelVm(FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpbSsp(..)) | InstOp::MemCopyBytes { .. } | InstOp::MemCopyVal { .. } | InstOp::Store { .. } @@ -1075,16 +1074,16 @@ impl<'a, 'eng> InstructionInserter<'a, 'eng> { revert_val } - pub fn jmp_to_ssp(self) -> Value { - let jmp_to_ssp_val = Value::new_instruction( + pub fn jmpb_ssp(self, offset: Value) -> Value { + let ldc_exec = Value::new_instruction( self.context, self.block, - InstOp::FuelVm(FuelVmInstruction::JmpToSsp), + InstOp::FuelVm(FuelVmInstruction::JmpbSsp(offset)), ); self.context.blocks[self.block.0] .instructions - .push(jmp_to_ssp_val); - jmp_to_ssp_val + .push(ldc_exec); + ldc_exec } pub fn smo(self, recipient: Value, message: Value, message_size: Value, coins: Value) -> Value { diff --git a/sway-ir/src/optimize/fn_dedup.rs b/sway-ir/src/optimize/fn_dedup.rs index 3756bf65680..25c0085a0c5 100644 --- a/sway-ir/src/optimize/fn_dedup.rs +++ b/sway-ir/src/optimize/fn_dedup.rs @@ -154,7 +154,7 @@ fn hash_fn(context: &Context, function: Function, eq_class: &mut EqClass) -> u64 crate::FuelVmInstruction::Log { log_ty, .. } => log_ty.hash(state), crate::FuelVmInstruction::ReadRegister(reg) => reg.hash(state), crate::FuelVmInstruction::Revert(_) - | crate::FuelVmInstruction::JmpToSsp + | crate::FuelVmInstruction::JmpbSsp(_) | crate::FuelVmInstruction::Smo { .. } | crate::FuelVmInstruction::StateClear { .. } | crate::FuelVmInstruction::StateLoadQuadWord { .. } diff --git a/sway-ir/src/optimize/inline.rs b/sway-ir/src/optimize/inline.rs index bcf6daa9622..50402b62d50 100644 --- a/sway-ir/src/optimize/inline.rs +++ b/sway-ir/src/optimize/inline.rs @@ -535,7 +535,9 @@ fn inline_instruction( new_block.append(context).read_register(reg) } FuelVmInstruction::Revert(val) => new_block.append(context).revert(map_value(val)), - FuelVmInstruction::JmpToSsp => new_block.append(context).jmp_to_ssp(), + FuelVmInstruction::JmpbSsp(offset) => { + new_block.append(context).jmpb_ssp(map_value(offset)) + } FuelVmInstruction::Smo { recipient, message, diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index ee9087398ca..48a4b75f089 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -656,9 +656,12 @@ fn instruction_to_doc<'a>( Doc::text(format!("revert {}", namer.name(context, v),)) .append(md_namer.md_idx_to_doc(context, metadata)), )), - FuelVmInstruction::JmpToSsp => Doc::line( - Doc::text("jmp_to_ssp").append(md_namer.md_idx_to_doc(context, metadata)), - ), + FuelVmInstruction::JmpbSsp(offset) => { + maybe_constant_to_doc(context, md_namer, namer, offset).append(Doc::line( + Doc::text(format!("jmpb_ssp {}", namer.name(context, offset),)) + .append(md_namer.md_idx_to_doc(context, metadata)), + )) + } FuelVmInstruction::Smo { recipient, message, diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 41bf3a15ccb..cfc899ea27e 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -233,7 +233,7 @@ impl<'a, 'eng> InstructionVerifier<'a, 'eng> { log_id, } => self.verify_log(log_val, log_ty, log_id)?, FuelVmInstruction::ReadRegister(_) => (), - FuelVmInstruction::JmpToSsp => (), + FuelVmInstruction::JmpbSsp(_) => (), FuelVmInstruction::Revert(val) => self.verify_revert(val)?, FuelVmInstruction::Smo { recipient, From fd1ca84b3a330bb8c0e48ceb20a8b40b15227417 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Fri, 16 Feb 2024 12:11:15 +0530 Subject: [PATCH 3/6] Some variable name fixes and test --- sway-core/src/ir_generation/function.rs | 4 +- .../ast_node/expression/intrinsic_function.rs | 14 ++--- sway-ir/src/block.rs | 5 +- sway-ir/src/instruction.rs | 2 +- sway-ir/src/parser.rs | 11 ++++ sway-ir/src/value.rs | 2 +- test/src/ir_generation/README.md | 8 +++ test/src/ir_generation/tests/jmpb_ssp.sw | 62 +++++++++++++++++++ 8 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 test/src/ir_generation/tests/jmpb_ssp.sw diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index d89123ee079..b1e7bc0b565 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -1095,7 +1095,7 @@ impl<'eng> FnCompiler<'eng> { Ok(TerminatorValue::new(val, context)) } Intrinsic::JmpbSsp => { - let contr_id_val = return_on_termination_or_extract!( + let offset_val = return_on_termination_or_extract!( self.compile_expression_to_value(context, md_mgr, &arguments[0])? ); @@ -1103,7 +1103,7 @@ impl<'eng> FnCompiler<'eng> { let val = self .current_block .append(context) - .jmpb_ssp(contr_id_val) + .jmpb_ssp(offset_val) .add_metadatum(context, span_md_idx); Ok(TerminatorValue::new(val, context)) } 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 00fc4d7715c..defd674b469 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 @@ -84,7 +84,7 @@ impl ty::TyIntrinsicFunctionKind { Intrinsic::Smo => type_check_smo(handler, ctx, kind, arguments, type_arguments, span), Intrinsic::Not => type_check_not(handler, ctx, kind, arguments, type_arguments, span), Intrinsic::JmpbSsp => { - type_check_ldc_exec(handler, ctx, kind, arguments, type_arguments, span) + type_check_jmpb_ssp(handler, ctx, kind, arguments, type_arguments, span) } } } @@ -1174,10 +1174,10 @@ fn type_check_revert( )) } -/// Signature: `__ldc_exec(contr_id: ContractId)` +/// Signature: `__jmpb_ssp(offset: u64)` /// Description: Loads contract and transfers control to it. -/// Constraints: contr_id has type `ContractId`. -fn type_check_ldc_exec( +/// Constraints: offset has type `u64`. +fn type_check_jmpb_ssp( handler: &Handler, mut ctx: TypeCheckContext, kind: sway_ast::Intrinsic, @@ -1204,18 +1204,18 @@ fn type_check_ldc_exec( })); } - // Type check the argument which is the revert code + // Type check the argument which is the jmpb_ssp offset let mut ctx = ctx.by_ref().with_type_annotation(type_engine.insert( engines, TypeInfo::UnsignedInteger(IntegerBits::SixtyFour), None, )); - let contr_id = ty::TyExpression::type_check(handler, ctx.by_ref(), arguments[0].clone())?; + let offset = ty::TyExpression::type_check(handler, ctx.by_ref(), arguments[0].clone())?; Ok(( ty::TyIntrinsicFunctionKind { kind, - arguments: vec![contr_id], + arguments: vec![offset], type_arguments: vec![], span, }, diff --git a/sway-ir/src/block.rs b/sway-ir/src/block.rs index a5228ab4c13..a864bce58a3 100644 --- a/sway-ir/src/block.rs +++ b/sway-ir/src/block.rs @@ -368,7 +368,10 @@ impl Block { matches!( i, Instruction { - op: InstOp::Ret(..) | InstOp::FuelVm(FuelVmInstruction::Revert(..)), + op: InstOp::Ret(..) + | InstOp::FuelVm( + FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpbSsp(..) + ), .. } ) diff --git a/sway-ir/src/instruction.rs b/sway-ir/src/instruction.rs index f5179033069..ba6bc8ce372 100644 --- a/sway-ir/src/instruction.rs +++ b/sway-ir/src/instruction.rs @@ -666,7 +666,7 @@ impl InstOp { InstOp::Branch(_) | InstOp::ConditionalBranch { .. } | InstOp::Ret(..) - | InstOp::FuelVm(FuelVmInstruction::Revert(..)) + | InstOp::FuelVm(FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpbSsp(..)) ) } } diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index d7427848284..526898f559f 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -203,6 +203,7 @@ mod ir_builder { / op_read_register() / op_ret() / op_revert() + / op_jmpb_ssp() / op_smo() / op_state_load_quad_word() / op_state_load_word() @@ -362,6 +363,11 @@ mod ir_builder { IrAstOperation::Revert(vn) } + rule op_jmpb_ssp() -> IrAstOperation + = "jmpb_ssp" _ vn:id() { + IrAstOperation::JmpbSsp(vn) + } + rule op_smo() -> IrAstOperation = "smo" _ recipient_and_message:id() comma() message_size:id() comma() output_index:id() comma() coins:id() _ { @@ -722,6 +728,7 @@ mod ir_builder { ReadRegister(String), Ret(IrAstTy, String), Revert(String), + JmpbSsp(String), Smo(String, String, String, String), StateClear(String, String), StateLoadQuadWord(String, String, String), @@ -1344,6 +1351,10 @@ mod ir_builder { .append(context) .revert(*val_map.get(&ret_val_name).unwrap()) .add_metadatum(context, opt_metadata), + IrAstOperation::JmpbSsp(offset_name) => block + .append(context) + .jmpb_ssp(*val_map.get(&offset_name).unwrap()) + .add_metadatum(context, opt_metadata), IrAstOperation::Smo(recipient, message, message_size, coins) => block .append(context) .smo( diff --git a/sway-ir/src/value.rs b/sway-ir/src/value.rs index 83d4eff7590..a912c22a4e9 100644 --- a/sway-ir/src/value.rs +++ b/sway-ir/src/value.rs @@ -122,7 +122,7 @@ impl Value { InstOp::Branch(_) | InstOp::ConditionalBranch { .. } | InstOp::Ret(_, _) - | InstOp::FuelVm(FuelVmInstruction::Revert(_)) + | InstOp::FuelVm(FuelVmInstruction::Revert(_) | FuelVmInstruction::JmpbSsp(_)) ), ValueDatum::Argument(..) | ValueDatum::Configurable(..) | ValueDatum::Constant(..) => { false diff --git a/test/src/ir_generation/README.md b/test/src/ir_generation/README.md index 81f15e8a4a9..7b2ac228bcb 100644 --- a/test/src/ir_generation/README.md +++ b/test/src/ir_generation/README.md @@ -28,6 +28,14 @@ Both checks against IR and ASM may be provided in the same Sway test source file To delimit between checks against IR and those against ASM the source file may be split into sections using delimiting marker text. * `::check-ir::` marks the beginning of the IR checks. +* `::check-ir-optimized::` marks the begingging of the optimized IR checks. * `::check-asm::` marks the beginning of the ASM checks. + Optimized IR chekcer can be configured with `pass: `. When + `o1` is chosen, all the configured passes are chosen automatically. + ``` + ::check-ir-optimized:: + pass: o1 + ``` + The sections may go in either order. If there are no markers then it is assumed that all checks are for IR. diff --git a/test/src/ir_generation/tests/jmpb_ssp.sw b/test/src/ir_generation/tests/jmpb_ssp.sw new file mode 100644 index 00000000000..55969c622b2 --- /dev/null +++ b/test/src/ir_generation/tests/jmpb_ssp.sw @@ -0,0 +1,62 @@ +// target-fuelvm + +contract; + +pub struct ContractId { + /// The underlying raw `b256` data of the contract id. + pub value: b256, +} + +abi MyContract { + fn test_function(code_id: ContractId); +} + +impl MyContract for Contract { + fn test_function(code_id_p: ContractId) { + let length = asm(code_id, length, word, ssp_saved) { + // Allocate 32 bytes on the heap (we can't use the stack) + addi word zero i32; + aloc word; + + lw code_id fp i74; + + // Log the ContractID for debugging + logd zero zero code_id word; + + // Load the entire contract with LDC + csiz length code_id; + // Save the old ssp + move ssp_saved ssp; + ldc code_id zero length; + // return the ssp difference, to feed __jmpb_ssp. + // This need not always be equal to `length` as `ldc` pads the `length`. + sub length ssp ssp_saved; + length: u64 + }; + __jmpb_ssp(length) + } +} + +// ::check-ir:: + +// check: pub entry fn test_function<72a09f5b> + +// ::check-ir-optimized:: +// pass: o1 + +// check: pub entry fn test_function +// not: local +// check: csiz length code_id, !7 +// check: ldc code_id zero length, +// check: jmpb_ssp + +// ::check-asm:: + +// regex: REG=.r\d+\b + +// check: csiz $(len=$REG) $REG +// check: ldc $REG $$zero $len +// check: sub $(old_ssp=$REG) $$ssp $REG ; jmpb_ssp: Compute $$ssp - offset +// sub $(jmp_target_4=$REG) $old_ssp $$is ; jmpb_ssp: Subtract $$is since $$jmp adds it back +// divi $(jmp_target=$REG) $jmp_target_4 i4 ; jmpb_ssp: Divide by 4 since Jmp multiplies by 4 +// jmp $jmp_target ; jmpb_ssp: Jump to computed value From 31319a8838381ccee34974f6d5b169484756cec6 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Sun, 18 Feb 2024 09:34:07 +0530 Subject: [PATCH 4/6] doc update --- docs/book/src/reference/compiler_intrinsics.md | 9 +++++++++ .../ast_node/expression/intrinsic_function.rs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/book/src/reference/compiler_intrinsics.md b/docs/book/src/reference/compiler_intrinsics.md index 5ae7bb7fba3..9e9b216d573 100644 --- a/docs/book/src/reference/compiler_intrinsics.md +++ b/docs/book/src/reference/compiler_intrinsics.md @@ -316,3 +316,12 @@ __not(op: T) -> T **Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`, `b256`. ___ + +```sway +__jmpb_ssp(offset: u64) +``` +**Description:** Jumps tp `$ssp - offset`. When the offset is the growth +of `$ssp` after an `ldc` call, this transfers control to the newly loaded +contract. + +**Constraints:** offset must have type `u64`. \ No newline at end of file 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 defd674b469..9bb96b45ed7 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 @@ -1175,7 +1175,7 @@ fn type_check_revert( } /// Signature: `__jmpb_ssp(offset: u64)` -/// Description: Loads contract and transfers control to it. +/// Description: Jumps tp `$ssp - offset`. /// Constraints: offset has type `u64`. fn type_check_jmpb_ssp( handler: &Handler, From f9fe0309195614b2d9fbbba3076f51aaabb4b8ee Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Sun, 18 Feb 2024 21:32:24 +0530 Subject: [PATCH 5/6] CI doc fixes --- docs/book/src/reference/compiler_intrinsics.md | 4 +++- .../ast_node/expression/intrinsic_function.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/book/src/reference/compiler_intrinsics.md b/docs/book/src/reference/compiler_intrinsics.md index 9e9b216d573..d1490bc55c3 100644 --- a/docs/book/src/reference/compiler_intrinsics.md +++ b/docs/book/src/reference/compiler_intrinsics.md @@ -315,12 +315,14 @@ __not(op: T) -> T **Description:** Bitwise NOT of `op` **Constraints:** `T` is an integer type, i.e. `u8`, `u16`, `u32`, `u64`, `u256`, `b256`. + ___ ```sway __jmpb_ssp(offset: u64) ``` -**Description:** Jumps tp `$ssp - offset`. When the offset is the growth + +**Description:** Jumps to `$ssp - offset`. When the offset is the growth of `$ssp` after an `ldc` call, this transfers control to the newly loaded contract. 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 9bb96b45ed7..81b6b60659e 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 @@ -1175,7 +1175,7 @@ fn type_check_revert( } /// Signature: `__jmpb_ssp(offset: u64)` -/// Description: Jumps tp `$ssp - offset`. +/// Description: Jumps to `$ssp - offset`. /// Constraints: offset has type `u64`. fn type_check_jmpb_ssp( handler: &Handler, From a6460c8045dac83184662d4f1f0799b41a5681d1 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Sun, 18 Feb 2024 21:39:11 +0530 Subject: [PATCH 6/6] trailing newline --- docs/book/src/reference/compiler_intrinsics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/book/src/reference/compiler_intrinsics.md b/docs/book/src/reference/compiler_intrinsics.md index d1490bc55c3..a8126bec0c4 100644 --- a/docs/book/src/reference/compiler_intrinsics.md +++ b/docs/book/src/reference/compiler_intrinsics.md @@ -326,4 +326,4 @@ __jmpb_ssp(offset: u64) of `$ssp` after an `ldc` call, this transfers control to the newly loaded contract. -**Constraints:** offset must have type `u64`. \ No newline at end of file +**Constraints:** offset must have type `u64`.