Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide __jmpb_ssp intrinsic #5576

Merged
merged 15 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions sway-ast/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub enum Intrinsic {
PtrSub,
Smo,
Not,
JmpbSsp,
}

impl fmt::Display for Intrinsic {
Expand Down Expand Up @@ -73,6 +74,7 @@ impl fmt::Display for Intrinsic {
Intrinsic::PtrSub => "ptr_sub",
Intrinsic::Smo => "smo",
Intrinsic::Not => "not",
Intrinsic::JmpbSsp => "jmpb_ssp",
};
write!(f, "{s}")
}
Expand Down Expand Up @@ -115,6 +117,7 @@ impl Intrinsic {
"__ptr_sub" => PtrSub,
"__smo" => Smo,
"__not" => Not,
"__jmpb_ssp" => JmpbSsp,
_ => return None,
})
}
Expand Down
45 changes: 45 additions & 0 deletions sway-core/src/asm_generation/fuel/fuel_asm_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::JmpbSsp(offset) => self.compile_jmpb_ssp(instr_val, offset),
},
InstOp::GetElemPtr {
base,
Expand Down Expand Up @@ -1451,6 +1452,50 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> {
Ok(())
}

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(by4_reg)),
comment: "jmpb_ssp: Jump to computed value".into(),
});

Ok(())
}

fn compile_smo(
&mut self,
instr_val: &Value,
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/ir_generation/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,7 @@ fn const_eval_intrinsic(
| Intrinsic::StateStoreQuad
| Intrinsic::Log
| Intrinsic::Revert
| Intrinsic::JmpbSsp
| Intrinsic::Smo => Err(ConstEvalError::CannotBeEvaluatedToConst {
span: intrinsic.span.clone(),
}),
Expand Down
13 changes: 13 additions & 0 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,19 @@ impl<'eng> FnCompiler<'eng> {
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(val, context))
}
Intrinsic::JmpbSsp => {
let offset_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)
.jmpb_ssp(offset_val)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(val, context))
}
Intrinsic::PtrAdd | Intrinsic::PtrSub => {
let op = match kind {
Intrinsic::PtrAdd => BinaryOpKind::Add,
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/language/ty/expression/intrinsic_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::JmpbSsp)
|| self
.arguments
.iter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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::JmpbSsp => {
type_check_jmpb_ssp(handler, ctx, kind, arguments, type_arguments, span)
}
}
}
}
Expand Down Expand Up @@ -1171,6 +1174,55 @@ fn type_check_revert(
))
}

/// Signature: `__jmpb_ssp(offset: u64)`
/// Description: Loads contract and transfers control to it.
/// Constraints: offset has type `u64`.
fn type_check_jmpb_ssp(
handler: &Handler,
mut ctx: TypeCheckContext,
kind: sway_ast::Intrinsic,
arguments: Vec<Expression>,
type_arguments: Vec<TypeArgument>,
span: Span,
) -> Result<(ty::TyIntrinsicFunctionKind, TypeId), ErrorEmitted> {
let type_engine = ctx.engines.te();
let engines = ctx.engines();

if arguments.len() != 1 {
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,
}));
}

// 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 offset = ty::TyExpression::type_check(handler, ctx.by_ref(), arguments[0].clone())?;

Ok((
ty::TyIntrinsicFunctionKind {
kind,
arguments: vec![offset],
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.
Expand Down
2 changes: 1 addition & 1 deletion sway-core/src/semantic_analysis/cei_pattern_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ fn effects_of_intrinsic(intr: &sway_ast::Intrinsic) -> HashSet<Effect> {
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 | 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(),
}
Expand Down
3 changes: 2 additions & 1 deletion sway-ir/src/analysis/memory_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ pub fn get_loaded_ptr_values(context: &Context, val: Value) -> Vec<Value> {
InstOp::Store { dst_val_ptr: _, .. } => vec![],
InstOp::FuelVm(FuelVmInstruction::Gtf { .. })
| InstOp::FuelVm(FuelVmInstruction::ReadRegister(_))
| InstOp::FuelVm(FuelVmInstruction::Revert(_)) => 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, .. }) => {
Expand Down Expand Up @@ -252,6 +252,7 @@ pub fn get_stored_ptr_values(context: &Context, val: Value) -> Vec<Value> {
| FuelVmInstruction::Log { .. }
| FuelVmInstruction::ReadRegister(_)
| FuelVmInstruction::Revert(_)
| FuelVmInstruction::JmpbSsp(_)
| FuelVmInstruction::Smo { .. }
| FuelVmInstruction::StateClear { .. } => vec![],
FuelVmInstruction::StateLoadQuadWord { load_val, .. } => vec![*load_val],
Expand Down
5 changes: 4 additions & 1 deletion sway-ir/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..)
),
..
}
)
Expand Down
22 changes: 18 additions & 4 deletions sway-ir/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ pub enum FuelVmInstruction {
arg1: Value,
arg2: Value,
},
JmpbSsp(Value),
}

/// Comparison operations.
Expand Down Expand Up @@ -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::JmpbSsp(..))
| InstOp::Ret(..) => None,

// No-op is also no-type.
Expand Down Expand Up @@ -407,7 +408,7 @@ impl InstOp {
log_val, log_id, ..
} => vec![*log_val, *log_id],
FuelVmInstruction::ReadRegister(_) => vec![],
FuelVmInstruction::Revert(v) => vec![*v],
FuelVmInstruction::Revert(v) | FuelVmInstruction::JmpbSsp(v) => vec![*v],
FuelVmInstruction::Smo {
recipient,
message,
Expand Down Expand Up @@ -544,6 +545,7 @@ impl InstOp {
}
FuelVmInstruction::ReadRegister { .. } => (),
FuelVmInstruction::Revert(revert_val) => replace(revert_val),
FuelVmInstruction::JmpbSsp(contr_id) => replace(contr_id),
FuelVmInstruction::Smo {
recipient,
message,
Expand Down Expand Up @@ -629,7 +631,7 @@ impl InstOp {
| InstOp::FuelVm(FuelVmInstruction::StateLoadQuadWord { .. })
| InstOp::FuelVm(FuelVmInstruction::StateStoreQuadWord { .. })
| InstOp::FuelVm(FuelVmInstruction::StateStoreWord { .. })
| InstOp::FuelVm(FuelVmInstruction::Revert(..))
| InstOp::FuelVm(FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpbSsp(..))
| InstOp::MemCopyBytes { .. }
| InstOp::MemCopyVal { .. }
| InstOp::Store { .. }
Expand Down Expand Up @@ -664,7 +666,7 @@ impl InstOp {
InstOp::Branch(_)
| InstOp::ConditionalBranch { .. }
| InstOp::Ret(..)
| InstOp::FuelVm(FuelVmInstruction::Revert(..))
| InstOp::FuelVm(FuelVmInstruction::Revert(..) | FuelVmInstruction::JmpbSsp(..))
)
}
}
Expand Down Expand Up @@ -1072,6 +1074,18 @@ impl<'a, 'eng> InstructionInserter<'a, 'eng> {
revert_val
}

pub fn jmpb_ssp(self, offset: Value) -> Value {
let ldc_exec = Value::new_instruction(
self.context,
self.block,
InstOp::FuelVm(FuelVmInstruction::JmpbSsp(offset)),
);
self.context.blocks[self.block.0]
.instructions
.push(ldc_exec);
ldc_exec
}

pub fn smo(self, recipient: Value, message: Value, message_size: Value, coins: Value) -> Value {
insert_instruction!(
self,
Expand Down
1 change: 1 addition & 0 deletions sway-ir/src/optimize/fn_dedup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::JmpbSsp(_)
| crate::FuelVmInstruction::Smo { .. }
| crate::FuelVmInstruction::StateClear { .. }
| crate::FuelVmInstruction::StateLoadQuadWord { .. }
Expand Down
3 changes: 3 additions & 0 deletions sway-ir/src/optimize/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,9 @@ fn inline_instruction(
new_block.append(context).read_register(reg)
}
FuelVmInstruction::Revert(val) => new_block.append(context).revert(map_value(val)),
FuelVmInstruction::JmpbSsp(offset) => {
new_block.append(context).jmpb_ssp(map_value(offset))
}
FuelVmInstruction::Smo {
recipient,
message,
Expand Down
11 changes: 11 additions & 0 deletions sway-ir/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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() _ {
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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(
Expand Down
6 changes: 6 additions & 0 deletions sway-ir/src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +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::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,
Expand Down
2 changes: 1 addition & 1 deletion sway-ir/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions sway-ir/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ impl<'a, 'eng> InstructionVerifier<'a, 'eng> {
log_id,
} => self.verify_log(log_val, log_ty, log_id)?,
FuelVmInstruction::ReadRegister(_) => (),
FuelVmInstruction::JmpbSsp(_) => (),
FuelVmInstruction::Revert(val) => self.verify_revert(val)?,
FuelVmInstruction::Smo {
recipient,
Expand Down
8 changes: 8 additions & 0 deletions test/src/ir_generation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: <PASSNAME or o1>`. 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.
Loading
Loading