Skip to content

Commit

Permalink
Add a new __gtf intrinsic (FuelLabs#2315)
Browse files Browse the repository at this point in the history
* Add int_to_ptr IR instruction

* Simplify codegen for int_to_ptr

* Update a few more things and add a test

* Add a new __gtf intrinsic

* Add a simple test

* Use int_to_ptr to generate asm for __gtf

* Add an ir_generation test

* Update sway-ir/src/verify.rs

Co-authored-by: Toby Hutton <[email protected]>

* Addressing some comments

* Update test/src/e2e_vm_tests/test_programs/should_pass/language/gtf_intrinsic/src/main.sw

Co-authored-by: Vaivaswatha N <[email protected]>

* Update sway-core/src/asm_generation/from_ir.rs

Co-authored-by: Toby Hutton <[email protected]>

* Update sway-ir/src/parser.rs

Co-authored-by: Toby Hutton <[email protected]>

* rename the test.. the old name was wrong

* Updating type checking of gtf to only expect u64s

* fmt

Co-authored-by: Toby Hutton <[email protected]>
Co-authored-by: Vaivaswatha N <[email protected]>
  • Loading branch information
3 people authored Jul 19, 2022
1 parent 6444f22 commit 9efe316
Show file tree
Hide file tree
Showing 19 changed files with 330 additions and 5 deletions.
19 changes: 19 additions & 0 deletions sway-core/src/asm_generation/from_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,9 @@ impl<'ir> AsmBuilder<'ir> {
ptr_ty,
offset,
} => self.compile_get_pointer(instr_val, base_ptr, ptr_ty, *offset),
Instruction::Gtf { index, tx_field_id } => {
self.compile_gtf(instr_val, index, *tx_field_id)
}
Instruction::InsertElement {
array,
ty,
Expand Down Expand Up @@ -1168,6 +1171,22 @@ impl<'ir> AsmBuilder<'ir> {
}
}

fn compile_gtf(&mut self, instr_val: &Value, index: &Value, tx_field_id: u64) {
let instr_reg = self.reg_seqr.next();
let index_reg = self.value_to_register(index);
self.bytecode.push(Op {
opcode: either::Either::Left(VirtualOp::GTF(
instr_reg,
index_reg,
VirtualImmediate12 {
value: tx_field_id as u16,
},
)),
comment: "get transaction field".into(),
owning_span: instr_val.get_span(self.context),
});
}

fn compile_insert_element(
&mut self,
instr_val: &Value,
Expand Down
3 changes: 3 additions & 0 deletions sway-core/src/asm_lang/allocated_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub(crate) enum AllocatedOpcode {
EXP(AllocatedRegister, AllocatedRegister, AllocatedRegister),
EXPI(AllocatedRegister, AllocatedRegister, VirtualImmediate12),
GT(AllocatedRegister, AllocatedRegister, AllocatedRegister),
GTF(AllocatedRegister, AllocatedRegister, VirtualImmediate12),
LT(AllocatedRegister, AllocatedRegister, AllocatedRegister),
MLOG(AllocatedRegister, AllocatedRegister, AllocatedRegister),
MROO(AllocatedRegister, AllocatedRegister, AllocatedRegister),
Expand Down Expand Up @@ -199,6 +200,7 @@ impl fmt::Display for AllocatedOp {
EXP(a, b, c) => format!("exp {} {} {}", a, b, c),
EXPI(a, b, c) => format!("expi {} {} {}", a, b, c),
GT(a, b, c) => format!("gt {} {} {}", a, b, c),
GTF(a, b, c) => format!("gtf {} {} {}", a, b, c),
LT(a, b, c) => format!("lt {} {} {}", a, b, c),
MLOG(a, b, c) => format!("mlog {} {} {}", a, b, c),
MROO(a, b, c) => format!("mroo {} {} {}", a, b, c),
Expand Down Expand Up @@ -309,6 +311,7 @@ impl AllocatedOp {
EXP (a, b, c) => VmOp::EXP (a.to_register_id(), b.to_register_id(), c.to_register_id()),
EXPI(a, b, c) => VmOp::EXPI(a.to_register_id(), b.to_register_id(), c.value),
GT (a, b, c) => VmOp::GT (a.to_register_id(), b.to_register_id(), c.to_register_id()),
GTF (a, b, c) => VmOp::GTF (a.to_register_id(), b.to_register_id(), c.value),
LT (a, b, c) => VmOp::LT (a.to_register_id(), b.to_register_id(), c.to_register_id()),
MLOG(a, b, c) => VmOp::MLOG(a.to_register_id(), b.to_register_id(), c.to_register_id()),
MROO(a, b, c) => VmOp::MROO(a.to_register_id(), b.to_register_id(), c.to_register_id()),
Expand Down
10 changes: 10 additions & 0 deletions sway-core/src/asm_lang/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,15 @@ impl Op {
);
VirtualOp::GT(r1, r2, r3)
}
"gtf" => {
let (r1, r2, imm) = check!(
two_regs_imm_12(args, immediate, whole_op_span),
return err(warnings, errors),
warnings,
errors
);
VirtualOp::GTF(r1, r2, imm)
}
"lt" => {
let (r1, r2, r3) = check!(
three_regs(args, immediate, whole_op_span),
Expand Down Expand Up @@ -1338,6 +1347,7 @@ impl fmt::Display for Op {
EXP(a, b, c) => format!("exp {} {} {}", a, b, c),
EXPI(a, b, c) => format!("expi {} {} {}", a, b, c),
GT(a, b, c) => format!("gt {} {} {}", a, b, c),
GTF(a, b, c) => format!("gt {} {} {}", a, b, c),
LT(a, b, c) => format!("lt {} {} {}", a, b, c),
MLOG(a, b, c) => format!("mlog {} {} {}", a, b, c),
MROO(a, b, c) => format!("mroo {} {} {}", a, b, c),
Expand Down
14 changes: 14 additions & 0 deletions sway-core/src/asm_lang/virtual_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub(crate) enum VirtualOp {
EXP(VirtualRegister, VirtualRegister, VirtualRegister),
EXPI(VirtualRegister, VirtualRegister, VirtualImmediate12),
GT(VirtualRegister, VirtualRegister, VirtualRegister),
GTF(VirtualRegister, VirtualRegister, VirtualImmediate12),
LT(VirtualRegister, VirtualRegister, VirtualRegister),
MLOG(VirtualRegister, VirtualRegister, VirtualRegister),
MROO(VirtualRegister, VirtualRegister, VirtualRegister),
Expand Down Expand Up @@ -164,6 +165,7 @@ impl VirtualOp {
EXP(r1, r2, r3) => vec![r1, r2, r3],
EXPI(r1, r2, _i) => vec![r1, r2],
GT(r1, r2, r3) => vec![r1, r2, r3],
GTF(r1, r2, _i) => vec![r1, r2],
LT(r1, r2, r3) => vec![r1, r2, r3],
MLOG(r1, r2, r3) => vec![r1, r2, r3],
MROO(r1, r2, r3) => vec![r1, r2, r3],
Expand Down Expand Up @@ -259,6 +261,7 @@ impl VirtualOp {
EXP(_r1, r2, r3) => vec![r2, r3],
EXPI(_r1, r2, _i) => vec![r2],
GT(_r1, r2, r3) => vec![r2, r3],
GTF(_r1, r2, _i) => vec![r2],
LT(_r1, r2, r3) => vec![r2, r3],
MLOG(_r1, r2, r3) => vec![r2, r3],
MROO(_r1, r2, r3) => vec![r2, r3],
Expand Down Expand Up @@ -354,6 +357,7 @@ impl VirtualOp {
EXP(r1, _r2, _r3) => vec![r1],
EXPI(r1, _r2, _i) => vec![r1],
GT(r1, _r2, _r3) => vec![r1],
GTF(r1, _r2, _i) => vec![r1],
LT(r1, _r2, _r3) => vec![r1],
MLOG(r1, _r2, _r3) => vec![r1],
MROO(r1, _r2, _r3) => vec![r1],
Expand Down Expand Up @@ -547,6 +551,11 @@ impl VirtualOp {
update_reg(reg_to_reg_map, r2),
update_reg(reg_to_reg_map, r3),
),
GTF(r1, r2, i) => Self::GTF(
update_reg(reg_to_reg_map, r1),
update_reg(reg_to_reg_map, r2),
i.clone(),
),
LT(r1, r2, r3) => Self::LT(
update_reg(reg_to_reg_map, r1),
update_reg(reg_to_reg_map, r2),
Expand Down Expand Up @@ -949,6 +958,11 @@ impl VirtualOp {
map_reg(&mapping, reg2),
map_reg(&mapping, reg3),
),
GTF(reg1, reg2, imm) => AllocatedOpcode::GTF(
map_reg(&mapping, reg1),
map_reg(&mapping, reg2),
imm.clone(),
),
LT(reg1, reg2, reg3) => AllocatedOpcode::LT(
map_reg(&mapping, reg1),
map_reg(&mapping, reg2),
Expand Down
52 changes: 51 additions & 1 deletion sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use crate::{
asm_generation::from_ir::ir_type_size_in_bytes,
constants,
error::CompileError,
ir_generation::const_eval::compile_constant_expression,
ir_generation::const_eval::{
compile_constant_expression, compile_constant_expression_to_constant,
},
parse_tree::{AsmOp, AsmRegister, LazyOp, Literal},
semantic_analysis::*,
type_engine::{resolve_type, TypeId, TypeInfo},
Expand Down Expand Up @@ -418,6 +420,54 @@ impl FnCompiler {
None,
))
}
Intrinsic::Gtf => {
// The index is just a Value
let index = self.compile_expression(context, arguments[0].clone())?;

// The tx field ID has to be a compile-time constant because it becomes an
// immediate
let tx_field_id_constant = compile_constant_expression_to_constant(
context,
self.module,
None,
&arguments[1],
)?;
let tx_field_id = match tx_field_id_constant.value {
ConstantValue::Uint(n) => n,
_ => {
return Err(CompileError::Internal(
"Transaction field ID for gtf intrinsic is not an integer. \
This should have been in caught in type checking",
span,
))
}
};

// Get the target type from the type argument provided
let target_type = type_arguments[0].clone();
let target_ir_type =
convert_resolved_typeid(context, &target_type.type_id, &target_type.span)?;

let span_md_idx = MetadataIndex::from_span(context, &span);

// The `gtf` instruction
let gtf_reg = self
.current_block
.ins(context)
.gtf(index, tx_field_id, span_md_idx);

// Reinterpret the result of th `gtf` instruction (which is always `u64`) as type
// `T`. This requires an `int_to_ptr` instruction if `T` is a reference type.
if target_ir_type.is_copy_type() {
Ok(gtf_reg)
} else {
Ok(self.current_block.ins(context).int_to_ptr(
gtf_reg,
target_ir_type,
span_md_idx,
))
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,95 @@ impl TypedIntrinsicFunctionKind {
insert_type(TypeInfo::Boolean),
)
}
Intrinsic::Gtf => {
if arguments.len() != 2 {
errors.push(CompileError::IntrinsicIncorrectNumArgs {
name: kind.to_string(),
expected: 2,
span,
});
return err(warnings, errors);
}

if type_arguments.len() != 1 {
errors.push(CompileError::IntrinsicIncorrectNumTArgs {
name: kind.to_string(),
expected: 1,
span,
});
return err(warnings, errors);
}

// Type check the first argument which is the index
let mut ctx = ctx
.by_ref()
.with_type_annotation(insert_type(TypeInfo::Unknown));
let index = check!(
TypedExpression::type_check(ctx.by_ref(), arguments[0].clone()),
return err(warnings, errors),
warnings,
errors
);

// Type check the second argument which is the tx field ID
let mut ctx = ctx
.by_ref()
.with_type_annotation(insert_type(TypeInfo::Unknown));
let tx_field_id = check!(
TypedExpression::type_check(ctx.by_ref(), arguments[1].clone()),
return err(warnings, errors),
warnings,
errors
);

// Make sure that the index argument is a `u64`
if !matches!(
resolve_type(index.return_type, &index.span).unwrap(),
TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)
) {
errors.push(CompileError::IntrinsicUnsupportedArgType {
name: kind.to_string(),
span: index.span.clone(),
});
}

// Make sure that the tx field ID is a `u64`
if !matches!(
resolve_type(tx_field_id.return_type, &tx_field_id.span).unwrap(),
TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)
) {
errors.push(CompileError::IntrinsicUnsupportedArgType {
name: kind.to_string(),
span: tx_field_id.span.clone(),
});
}

let targ = type_arguments[0].clone();
let type_id = check!(
ctx.resolve_type_with_self(
insert_type(resolve_type(targ.type_id, &targ.span).unwrap()),
&targ.span,
EnforceTypeArguments::Yes,
None
),
insert_type(TypeInfo::ErrorRecovery),
warnings,
errors,
);

(
TypedIntrinsicFunctionKind {
kind,
arguments: vec![index, tx_field_id],
type_arguments: vec![TypeArgument {
type_id,
span: targ.span,
}],
span,
},
type_id,
)
}
};
ok((intrinsic_function, return_type), warnings, errors)
}
Expand Down
5 changes: 5 additions & 0 deletions sway-ir/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub enum IrError {
VerifyStoreNonExistentPointer,
VerifyStoreToNonPointer,
VerifyUntypedValuePassedToFunction,
VerifyInvalidGtfIndexType,
}

impl std::error::Error for IrError {}
Expand Down Expand Up @@ -267,6 +268,10 @@ impl fmt::Display for IrError {
f,
"Verification failed: An untyped/void value has been passed to a function call."
),
IrError::VerifyInvalidGtfIndexType => write!(
f,
"Verification failed: An non-integer value has been passed to a 'gtf' instruction."
),
}
}
}
37 changes: 33 additions & 4 deletions sway-ir/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ pub enum Instruction {
},
/// Generate a unique integer value
GetStorageKey,
Gtf {
index: Value,
tx_field_id: u64,
},
/// Return a pointer as a value.
GetPointer {
base_ptr: Pointer,
Expand Down Expand Up @@ -95,17 +99,29 @@ pub enum Instruction {
/// Return from a function.
Ret(Value, Type),
/// Read a quad word from a storage slot. Type of `load_val` must be a B256 ptr.
StateLoadQuadWord { load_val: Value, key: Value },
StateLoadQuadWord {
load_val: Value,
key: Value,
},
/// Read a single word from a storage slot.
StateLoadWord(Value),
/// Write a value to a storage slot. Key must be a B256, type of `stored_val` must be a
/// Uint(256) ptr.
StateStoreQuadWord { stored_val: Value, key: Value },
StateStoreQuadWord {
stored_val: Value,
key: Value,
},
/// Write a value to a storage slot. Key must be a B256, type of `stored_val` must be a
/// Uint(64) value.
StateStoreWord { stored_val: Value, key: Value },
StateStoreWord {
stored_val: Value,
key: Value,
},
/// Write a value to a memory pointer.
Store { dst_val: Value, stored_val: Value },
Store {
dst_val: Value,
stored_val: Value,
},
}

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -163,6 +179,7 @@ impl Instruction {
Instruction::ExtractElement { ty, .. } => ty.get_elem_type(context),
Instruction::ExtractValue { ty, indices, .. } => ty.get_field_type(context, indices),
Instruction::GetStorageKey => Some(Type::B256),
Instruction::Gtf { .. } => Some(Type::Uint(64)),
Instruction::InsertElement { array, .. } => array.get_type(context),
Instruction::InsertValue { aggregate, .. } => aggregate.get_type(context),
Instruction::Load(ptr_val) => {
Expand Down Expand Up @@ -295,6 +312,7 @@ impl Instruction {
}
Instruction::ExtractValue { aggregate, .. } => replace(aggregate),
Instruction::GetStorageKey => (),
Instruction::Gtf { index, .. } => replace(index),
Instruction::IntToPtr(value, _) => replace(value),
Instruction::Load(_) => (),
Instruction::Nop => (),
Expand Down Expand Up @@ -611,6 +629,17 @@ impl<'a> InstructionInserter<'a> {
get_storage_key_val
}

pub fn gtf(self, index: Value, tx_field_id: u64, span_md_idx: Option<MetadataIndex>) -> Value {
let gtf_val = Value::new_instruction(
self.context,
Instruction::Gtf { index, tx_field_id },
span_md_idx,
None,
);
self.context.blocks[self.block.0].instructions.push(gtf_val);
gtf_val
}

pub fn get_ptr(
self,
base_ptr: Pointer,
Expand Down
Loading

0 comments on commit 9efe316

Please sign in to comment.