Skip to content

Commit

Permalink
Fix contract call args handling. (#1272)
Browse files Browse the repository at this point in the history
Re-introduce the special case where if there's a single argument to a
function call it is embedded directly in the call frame rather than
being a pointer to the args elsewhere.
  • Loading branch information
otrho authored Apr 17, 2022
1 parent 45cbe7e commit 61d77dd
Show file tree
Hide file tree
Showing 13 changed files with 401 additions and 349 deletions.
227 changes: 125 additions & 102 deletions sway-core/src/asm_generation/from_ir.rs

Large diffs are not rendered by default.

142 changes: 96 additions & 46 deletions sway-core/src/optimize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -822,46 +822,103 @@ impl FnCompiler {
.map(|(_, expr)| self.compile_expression(context, expr))
.collect::<Result<Vec<Value>, CompileError>>()?;

// New struct type to hold the user arguments
let field_types = compiled_args
.iter()
.map(|val| val.get_type(context).unwrap())
.collect::<Vec<_>>();
let user_args_struct_aggregate = Aggregate::new_struct(context, field_types);
let user_args_val = match compiled_args.len() {
0 => Constant::get_uint(context, 64, 0, None),
1 => {
// The single arg doesn't need to be put into a struct.
let arg0 = compiled_args[0];

// We're still undecided as to whether this should be decided by type or size.
// Going with type for now.
let arg0_type = arg0.get_type(context).unwrap();
if arg0_type.is_copy_type() {
arg0
} else {
// Copy this value to a new location. This is quite inefficient but we need to
// pass by reference rather than by value. Optimisation passes can remove all
// the unnecessary copying eventually, though it feels like we're jumping
// through a bunch of hoops here (employing the single arg optimisation) for
// minimal returns.
let by_reference_arg_name = self
.lexical_map
.insert(format!("{}{}", "arg_for_", ast_name));
let by_reference_arg = self
.function
.new_local_ptr(context, by_reference_arg_name, arg0_type, false, None)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::empty())
})?;

let arg0_ptr = self.current_block.ins(context).get_ptr(
by_reference_arg,
arg0_type,
0,
None,
);
self.current_block.ins(context).store(arg0_ptr, arg0, None);

// NOTE: Here we're fetching the original stack pointer, cast to u64.
self.current_block.ins(context).get_ptr(
by_reference_arg,
Type::Uint(64),
0,
span_md_idx,
)
}
}
_ => {
// New struct type to hold the user arguments bundled together.
let field_types = compiled_args
.iter()
.map(|val| val.get_type(context).unwrap())
.collect::<Vec<_>>();
let user_args_struct_aggregate = Aggregate::new_struct(context, field_types);

// New local pointer for the struct to hold all user arguments
let user_args_struct_local_name = self
.lexical_map
.insert(format!("{}{}", "args_struct_for_", ast_name));
let user_args_struct_ptr = self
.function
.new_local_ptr(
context,
user_args_struct_local_name,
Type::Struct(user_args_struct_aggregate),
true,
None,
)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::empty())
})?;

// New local pointer for the struct to hold all user arguments
let alias_user_args_struct_local_name = self
.lexical_map
.insert(format!("{}{}", "args_struct_for_", ast_name));
let user_args_struct_ptr = self
.function
.new_local_ptr(
context,
alias_user_args_struct_local_name,
Type::Struct(user_args_struct_aggregate),
true,
None,
)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::empty()))?;
// Initialise each of the fields in the user args struct.
compiled_args.into_iter().enumerate().fold(
self.current_block.ins(context).get_ptr(
user_args_struct_ptr,
Type::Struct(user_args_struct_aggregate),
0,
span_md_idx,
),
|user_args_struct_ptr_val, (insert_idx, insert_val)| {
self.current_block.ins(context).insert_value(
user_args_struct_ptr_val,
user_args_struct_aggregate,
insert_val,
vec![insert_idx as u64],
span_md_idx,
)
},
);

// Initialise each of the fields in the user args struct.
compiled_args.into_iter().enumerate().fold(
self.current_block.ins(context).get_ptr(
user_args_struct_ptr,
Type::Struct(user_args_struct_aggregate),
0,
span_md_idx,
),
|user_args_struct_ptr_val, (insert_idx, insert_val)| {
self.current_block.ins(context).insert_value(
user_args_struct_ptr_val,
user_args_struct_aggregate,
insert_val,
vec![insert_idx as u64],
// NOTE: Here we're fetching the original stack pointer, cast to u64.
self.current_block.ins(context).get_ptr(
user_args_struct_ptr,
Type::Uint(64),
0,
span_md_idx,
)
},
);
}
};

// Now handle the contract address and the selector. The contract address is just
// as B256 while the selector is a [u8; 4] which we have to convert to a U64.
Expand Down Expand Up @@ -900,19 +957,12 @@ impl FnCompiler {
span_md_idx,
);

// Insert the pointer to the user args struct.
//
// NOTE: Here we're inserting the original stack pointer, cast to u64.
let user_args_struct_addr_val = self.current_block.ins(context).get_ptr(
user_args_struct_ptr,
Type::Uint(64),
0,
span_md_idx,
);
// Insert the user args value.

ra_struct_val = self.current_block.ins(context).insert_value(
ra_struct_val,
ra_struct_aggregate,
user_args_struct_addr_val,
user_args_val,
vec![2],
span_md_idx,
);
Expand Down
2 changes: 0 additions & 2 deletions sway-core/tests/ir_to_asm/nested_single_word_struct.asm
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ eq $r0 $r1 $r0 ; function selector comparison
jnei $zero $r0 i11 ; jump to selected function
rvrt $zero ; revert if no selectors matched
lw $r0 $fp i74 ; Base register for method parameter
addi $r0 $r0 i0 ; Get address for arg input1
addi $r0 $r0 i0 ; extract address
lw $r0 $r0 i0 ; extract_value @ 0
ret $r0
noop ; word-alignment of data section
.data:
data_0 .u32 0x495d4a23
24 changes: 11 additions & 13 deletions sway-core/tests/ir_to_asm/simple_contract.asm
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,20 @@ DATA_SECTION_OFFSET[0..32]
DATA_SECTION_OFFSET[32..64]
lw $ds $is 1
add $$ds $$ds $is
lw $r1 $fp i73 ; load input function selector
lw $r0 data_2 ; load fn selector for comparison
eq $r0 $r1 $r0 ; function selector comparison
jnei $zero $r0 i17 ; jump to selected function
lw $r0 data_3 ; load fn selector for comparison
eq $r0 $r1 $r0 ; function selector comparison
jnei $zero $r0 i20 ; jump to selected function
lw $r0 data_4 ; load fn selector for comparison
eq $r0 $r1 $r0 ; function selector comparison
jnei $zero $r0 i24 ; jump to selected function
lw $r0 $fp i73 ; load input function selector
lw $r1 data_2 ; load fn selector for comparison
eq $r1 $r0 $r1 ; function selector comparison
jnei $zero $r1 i17 ; jump to selected function
lw $r1 data_3 ; load fn selector for comparison
eq $r1 $r0 $r1 ; function selector comparison
jnei $zero $r1 i19 ; jump to selected function
lw $r1 data_4 ; load fn selector for comparison
eq $r0 $r0 $r1 ; function selector comparison
jnei $zero $r0 i22 ; jump to selected function
rvrt $zero ; revert if no selectors matched
lw $r0 $fp i74 ; Base register for method parameter
lw $r0 $r0 i0 ; Get arg val
ret $r0
lw $r0 $fp i74 ; Base register for method parameter
addi $r1 $r0 i0 ; Get address for arg val
lw $r1 $fp i74 ; Base register for method parameter
lw $r0 data_0 ; loading size for RETD
retd $r1 $r0
lw $r1 $fp i74 ; Base register for method parameter
Expand Down
112 changes: 54 additions & 58 deletions sway-core/tests/sway_to_ir/simple_contract_call.ir
Original file line number Diff line number Diff line change
@@ -1,78 +1,74 @@
script {
fn main() -> u64 {
local ptr u64 a
local mut ptr { b256 } args_struct_for_get_b256
local ptr b256 arg_for_get_b256
local mut ptr { u64, b256 } args_struct_for_get_s
local mut ptr { u64 } args_struct_for_get_u64
local ptr b256 b
local ptr { u64, b256 } s

entry:
v0 = get_ptr mut ptr { u64 } args_struct_for_get_u64, ptr { u64 }, 0, !1
v1 = const u64 1111, !2
v2 = insert_value v0, { u64 }, v1, 0, !1
v3 = const { b256, u64, u64 } { b256 undef, u64 undef, u64 undef }, !1
v4 = const b256 0x0c1c50c2bf5ba4bb351b4249a2f5e7d86556fcb4a6ae90465ff6c86126eeb3c0, !3
v5 = insert_value v3, { b256, u64, u64 }, v4, 0, !1
v6 = const u64 2559618804, !1
v7 = insert_value v5, { b256, u64, u64 }, v6, 1, !1
v8 = get_ptr mut ptr { u64 } args_struct_for_get_u64, ptr u64, 0, !1
v9 = insert_value v7, { b256, u64, u64 }, v8, 2, !1
v10 = const u64 0, !4
v11 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000, !5
v12 = const u64 10000, !6
v13 = contract_call u64 get_u64 v9, v10, v11, v12, !1
v14 = get_ptr ptr u64 a, ptr u64, 0, !7
store v13, ptr v14, !7
v15 = get_ptr mut ptr { b256 } args_struct_for_get_b256, ptr { b256 }, 0, !8
v16 = const b256 0x3333333333333333333333333333333333333333333333333333333333333333, !9
v17 = insert_value v15, { b256 }, v16, 0, !8
v18 = const { b256, u64, u64 } { b256 undef, u64 undef, u64 undef }, !8
v19 = const b256 0x0c1c50c2bf5ba4bb351b4249a2f5e7d86556fcb4a6ae90465ff6c86126eeb3c0, !10
v20 = insert_value v18, { b256, u64, u64 }, v19, 0, !8
v21 = const u64 1108491158, !8
v22 = insert_value v20, { b256, u64, u64 }, v21, 1, !8
v23 = get_ptr mut ptr { b256 } args_struct_for_get_b256, ptr u64, 0, !8
v24 = insert_value v22, { b256, u64, u64 }, v23, 2, !8
v25 = const u64 0, !11
v26 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000, !12
v27 = const u64 20000, !13
v28 = contract_call b256 get_b256 v24, v25, v26, v27, !8
v29 = get_ptr ptr b256 b, ptr b256, 0, !14
store v28, ptr v29, !14
v30 = get_ptr mut ptr { u64, b256 } args_struct_for_get_s, ptr { u64, b256 }, 0, !15
v31 = const u64 5555, !16
v32 = insert_value v30, { u64, b256 }, v31, 0, !15
v33 = const b256 0x5555555555555555555555555555555555555555555555555555555555555555, !17
v34 = insert_value v32, { u64, b256 }, v33, 1, !15
v35 = const { b256, u64, u64 } { b256 undef, u64 undef, u64 undef }, !15
v36 = const b256 0x0c1c50c2bf5ba4bb351b4249a2f5e7d86556fcb4a6ae90465ff6c86126eeb3c0, !18
v37 = insert_value v35, { b256, u64, u64 }, v36, 0, !15
v38 = const u64 4234334249, !15
v39 = insert_value v37, { b256, u64, u64 }, v38, 1, !15
v40 = get_ptr mut ptr { u64, b256 } args_struct_for_get_s, ptr u64, 0, !15
v41 = insert_value v39, { b256, u64, u64 }, v40, 2, !15
v42 = read_register cgas, !15
v43 = const u64 0, !19
v44 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000, !20
v45 = contract_call { u64, b256 } get_s v41, v43, v44, v42, !15
v46 = get_ptr ptr { u64, b256 } s, ptr { u64, b256 }, 0, !21
store v45, ptr v46, !21
v47 = const u64 0, !22
ret u64 v47
v0 = const { b256, u64, u64 } { b256 undef, u64 undef, u64 undef }, !1
v1 = const b256 0x0c1c50c2bf5ba4bb351b4249a2f5e7d86556fcb4a6ae90465ff6c86126eeb3c0, !2
v2 = insert_value v0, { b256, u64, u64 }, v1, 0, !1
v3 = const u64 2559618804, !1
v4 = insert_value v2, { b256, u64, u64 }, v3, 1, !1
v5 = const u64 1111, !3
v6 = insert_value v4, { b256, u64, u64 }, v5, 2, !1
v7 = const u64 0, !4
v8 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000, !5
v9 = const u64 10000, !6
v10 = contract_call u64 get_u64 v6, v7, v8, v9, !1
v11 = get_ptr ptr u64 a, ptr u64, 0, !7
store v10, ptr v11, !7
v12 = get_ptr ptr b256 arg_for_get_b256, ptr b256, 0
v13 = const b256 0x3333333333333333333333333333333333333333333333333333333333333333, !8
store v13, ptr v12
v14 = get_ptr ptr b256 arg_for_get_b256, ptr u64, 0, !9
v15 = const { b256, u64, u64 } { b256 undef, u64 undef, u64 undef }, !9
v16 = const b256 0x0c1c50c2bf5ba4bb351b4249a2f5e7d86556fcb4a6ae90465ff6c86126eeb3c0, !10
v17 = insert_value v15, { b256, u64, u64 }, v16, 0, !9
v18 = const u64 1108491158, !9
v19 = insert_value v17, { b256, u64, u64 }, v18, 1, !9
v20 = insert_value v19, { b256, u64, u64 }, v14, 2, !9
v21 = const u64 0, !11
v22 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000, !12
v23 = const u64 20000, !13
v24 = contract_call b256 get_b256 v20, v21, v22, v23, !9
v25 = get_ptr ptr b256 b, ptr b256, 0, !14
store v24, ptr v25, !14
v26 = get_ptr mut ptr { u64, b256 } args_struct_for_get_s, ptr { u64, b256 }, 0, !15
v27 = const u64 5555, !16
v28 = insert_value v26, { u64, b256 }, v27, 0, !15
v29 = const b256 0x5555555555555555555555555555555555555555555555555555555555555555, !17
v30 = insert_value v28, { u64, b256 }, v29, 1, !15
v31 = get_ptr mut ptr { u64, b256 } args_struct_for_get_s, ptr u64, 0, !15
v32 = const { b256, u64, u64 } { b256 undef, u64 undef, u64 undef }, !15
v33 = const b256 0x0c1c50c2bf5ba4bb351b4249a2f5e7d86556fcb4a6ae90465ff6c86126eeb3c0, !18
v34 = insert_value v32, { b256, u64, u64 }, v33, 0, !15
v35 = const u64 4234334249, !15
v36 = insert_value v34, { b256, u64, u64 }, v35, 1, !15
v37 = insert_value v36, { b256, u64, u64 }, v31, 2, !15
v38 = read_register cgas, !15
v39 = const u64 0, !19
v40 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000, !20
v41 = contract_call { u64, b256 } get_s v37, v39, v40, v38, !15
v42 = get_ptr ptr { u64, b256 } s, ptr { u64, b256 }, 0, !21
store v41, ptr v42, !21
v43 = const u64 0, !22
ret u64 v43
}
}

!0 = filepath "/path/to/simple_contract_call.sw"
!1 = span !0 301 458
!2 = span !0 453 457
!3 = span !0 0 66
!2 = span !0 0 66
!3 = span !0 453 457
!4 = span !0 333 334
!5 = span !0 354 420
!6 = span !0 435 440
!7 = span !0 293 459
!8 = span !0 473 693
!9 = span !0 626 692
!8 = span !0 626 692
!9 = span !0 473 693
!10 = span !0 0 66
!11 = span !0 506 507
!12 = span !0 527 593
Expand Down
Loading

0 comments on commit 61d77dd

Please sign in to comment.