Skip to content

Commit

Permalink
Implement new copy semantics (#784)
Browse files Browse the repository at this point in the history
* rename Return[Nez]Many to Return[Nez]Span

* fix spelling issue

* adjust Instruction type for new copy encoding

This adds some instructions or instruction parameters to account for the new planned copy semantics that replace consecutive copy instructions with a single instruction that handles all the necessary copying between registers.
This will fix a bug that cannot be fixed with the current copy semantics that involves unavoidable overlapping copy instructions.

Newly added instruction parameters are:
- Register2
- Register3
- RegisterList

Newly added instructions are:
- ReturnReg2
- ReturnReg3
- ReturnMany
- ReturnNezReg2
- ReturnNezMany
- Copy2
- CopyMany

Removing one instruction parameter:
- CallParams

Also the PR associated to this commit will adjust call instruction parameter encodings.

This commit does not include execution implementations or execution implementation adjustments of the newly added or changed instructions respectively.

* implement execution of Instruction::Copy2

* implement execution of Instruction::CopyMany

* implement Instruction::ReturnReg{2,3} execution

* implement Instruction::ReturnNezReg2 execution

* replace path with use

* clean up of new copy execution implementation

* implement Instruction::Return[Nez]Many execution

* implement new call param copy semantics

Adjustments for instruction pointer updates is still missing that needs to be altered since amount of parameters is only discovered upon call param copying during execution and no longer before.

* no longer update instruction pointer for tail calls

This is not needed since the caller call frame is discarded during the operation anyways.

* remove ResolvedCallIndirectParams type

* improve panic message

* properly update instruction pointer on non-tail calls

* apply rustfmt

* add InstructionPtr::pull and use it where applicable

* refactor fetch_call_indirect_params

* refactor CallIndirectParams

Also move the Instruction variants to the other instruction parameter variants.

* add constructors for new Instruction::Register{1,2,3,List}

* add constructors for new instructions

* adjust call_[imported,internal] parameter encoding

* adjust Instruction::[return_]call_indirect encoding

* adjust Instruction::[return_]call translation and tests

* implement new encoding for return instructions

* remove no longer needed Instruction::CallParams

* adjust br_if encoding when conditionally returning

* implement new copy semantics for copy instructions

* remove invalid update to instr_ptr in call execution

* fix panic message

* remove InstructionPtr::pull method

Its use is discouraged since misusing it caused a bug in copying of call parameters.

* clean up call execution implementation a bit

* respect overlapping copy spans in execution implementation

* minor cleanup

* add copy_span instruction variant for non-overlapping spans

The non-overlapping copy_span variants can easily precomputed at compile time and does not require a temporary buffer to avoid invalid copy overwrites.

* add Instruction::CopyManyNonOverlapping variant

This is an optimized version of Instruction::CopyMany that does not require to store its values into a temporary buffer since it assumes that both results and values do not overlap. This assumption is asserted during compilation.

* rename test

* improve copy_span overlap detection

The new function does a better job at detecting actual copy overlaps. The former function checked if both spans overlapped without respecting the copy operation that only needs to check if the values are overwritten by the results.

* implement host function parameter passing

Parameter passing for host functions called from root are not yet implemented.

* add tests for calling host functions from the host side

* implement host function calling from host side through executor
  • Loading branch information
Robbepop authored Nov 16, 2023
1 parent 194359f commit 72be93b
Show file tree
Hide file tree
Showing 32 changed files with 4,700 additions and 1,070 deletions.
172 changes: 149 additions & 23 deletions crates/wasmi/src/engine/regmach/bytecode/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ use super::{
BinInstr,
BinInstrImm16,
CallIndirectParams,
CallIndirectParamsImm16,
CallParams,
Const16,
Const32,
Instruction,
Expand Down Expand Up @@ -185,6 +183,24 @@ impl Instruction {
}
}

/// Creates a new [`Instruction::ReturnReg2`] for the given [`Register`] indices.
pub fn return_reg2(reg0: impl Into<Register>, reg1: impl Into<Register>) -> Self {
Self::ReturnReg2 {
values: [reg0.into(), reg1.into()],
}
}

/// Creates a new [`Instruction::ReturnReg3`] for the given [`Register`] indices.
pub fn return_reg3(
reg0: impl Into<Register>,
reg1: impl Into<Register>,
reg2: impl Into<Register>,
) -> Self {
Self::ReturnReg3 {
values: [reg0.into(), reg1.into(), reg2.into()],
}
}

/// Creates a new [`Instruction::ReturnImm32`] from the given `value`.
pub fn return_imm32(value: impl Into<AnyConst32>) -> Self {
Self::ReturnImm32 {
Expand All @@ -206,19 +222,47 @@ impl Instruction {
}
}

/// Creates a new [`Instruction::ReturnMany`] from the given `values`.
pub fn return_many(values: RegisterSpanIter) -> Self {
Self::ReturnMany { values }
/// Creates a new [`Instruction::ReturnSpan`] from the given `values`.
pub fn return_span(values: RegisterSpanIter) -> Self {
Self::ReturnSpan { values }
}

/// Creates a new [`Instruction::ReturnMany`] for the given [`Register`] indices.
pub fn return_many(
reg0: impl Into<Register>,
reg1: impl Into<Register>,
reg2: impl Into<Register>,
) -> Self {
Self::ReturnMany {
values: [reg0.into(), reg1.into(), reg2.into()],
}
}

/// Creates a new [`Instruction::ReturnNez`] for the given `condition`.
pub fn return_nez(condition: Register) -> Self {
Self::ReturnNez { condition }
pub fn return_nez(condition: impl Into<Register>) -> Self {
Self::ReturnNez {
condition: condition.into(),
}
}

/// Creates a new [`Instruction::ReturnNezReg`] for the given `condition` and `value`.
pub fn return_nez_reg(condition: Register, value: Register) -> Self {
Self::ReturnNezReg { condition, value }
pub fn return_nez_reg(condition: impl Into<Register>, value: impl Into<Register>) -> Self {
Self::ReturnNezReg {
condition: condition.into(),
value: value.into(),
}
}

/// Creates a new [`Instruction::ReturnNezReg2`] for the given `condition` and `value`.
pub fn return_nez_reg2(
condition: impl Into<Register>,
value0: impl Into<Register>,
value1: impl Into<Register>,
) -> Self {
Self::ReturnNezReg2 {
condition: condition.into(),
values: [value0.into(), value1.into()],
}
}

/// Creates a new [`Instruction::ReturnNezImm32`] for the given `condition` and `value`.
Expand Down Expand Up @@ -246,8 +290,20 @@ impl Instruction {
}

/// Creates a new [`Instruction::ReturnNezMany`] for the given `condition` and `values`.
pub fn return_nez_many(condition: Register, values: RegisterSpanIter) -> Self {
Self::ReturnNezMany { condition, values }
pub fn return_nez_span(condition: Register, values: RegisterSpanIter) -> Self {
Self::ReturnNezSpan { condition, values }
}

/// Creates a new [`Instruction::ReturnNezMany`] for the given `condition` and `value`.
pub fn return_nez_many(
condition: impl Into<Register>,
head0: impl Into<Register>,
head1: impl Into<Register>,
) -> Self {
Self::ReturnNezMany {
condition: condition.into(),
values: [head0.into(), head1.into()],
}
}

/// Creates a new [`Instruction::Branch`] for the given `offset`.
Expand All @@ -274,8 +330,23 @@ impl Instruction {
}

/// Creates a new [`Instruction::Copy`].
pub fn copy(result: Register, value: Register) -> Self {
Self::Copy { result, value }
pub fn copy(result: impl Into<Register>, value: impl Into<Register>) -> Self {
Self::Copy {
result: result.into(),
value: value.into(),
}
}

/// Creates a new [`Instruction::Copy2`].
pub fn copy2(
results: RegisterSpan,
value0: impl Into<Register>,
value1: impl Into<Register>,
) -> Self {
Self::Copy2 {
results,
values: [value0.into(), value1.into()],
}
}

/// Creates a new [`Instruction::CopyImm32`].
Expand Down Expand Up @@ -312,6 +383,44 @@ impl Instruction {
}
}

/// Creates a new [`Instruction::CopySpanNonOverlapping`] copying multiple consecutive values.
pub fn copy_span_non_overlapping(
results: RegisterSpan,
values: RegisterSpan,
len: u16,
) -> Self {
debug_assert!(!results.iter_u16(len).is_overlapping(&values.iter_u16(len)));
Self::CopySpanNonOverlapping {
results,
values,
len,
}
}

/// Creates a new [`Instruction::CopyMany`].
pub fn copy_many(
results: RegisterSpan,
head0: impl Into<Register>,
head1: impl Into<Register>,
) -> Self {
Self::CopyMany {
results,
values: [head0.into(), head1.into()],
}
}

/// Creates a new [`Instruction::CopyManyNonOverlapping`].
pub fn copy_many_non_overlapping(
results: RegisterSpan,
head0: impl Into<Register>,
head1: impl Into<Register>,
) -> Self {
Self::CopyManyNonOverlapping {
results,
values: [head0.into(), head1.into()],
}
}

/// Creates a new [`Instruction::GlobalGet`].
pub fn global_get(result: Register, global: bytecode::GlobalIdx) -> Self {
Self::GlobalGet { result, global }
Expand Down Expand Up @@ -943,15 +1052,32 @@ impl Instruction {
}
}

/// Creates a new [`Instruction::CallParams`] for the given `params` and `len_results`.
pub fn call_params(params: RegisterSpanIter, len_results: u16) -> Self {
let len_params = params.len_as_u16();
let params = params.span();
Self::CallParams(CallParams {
params,
len_params,
len_results,
})
/// Creates a new [`Instruction::Register`] instruction parameter.
pub fn register(reg: impl Into<Register>) -> Self {
Self::Register(reg.into())
}

/// Creates a new [`Instruction::Register2`] instruction parameter.
pub fn register2(reg0: impl Into<Register>, reg1: impl Into<Register>) -> Self {
Self::Register2([reg0.into(), reg1.into()])
}

/// Creates a new [`Instruction::Register3`] instruction parameter.
pub fn register3(
reg0: impl Into<Register>,
reg1: impl Into<Register>,
reg2: impl Into<Register>,
) -> Self {
Self::Register3([reg0.into(), reg1.into(), reg2.into()])
}

/// Creates a new [`Instruction::RegisterList`] instruction parameter.
pub fn register_list(
reg0: impl Into<Register>,
reg1: impl Into<Register>,
reg2: impl Into<Register>,
) -> Self {
Self::RegisterList([reg0.into(), reg1.into(), reg2.into()])
}

/// Creates a new [`Instruction::CallIndirectParams`] for the given `index` and `table`.
Expand All @@ -967,7 +1093,7 @@ impl Instruction {
index: impl Into<Const16<u32>>,
table: impl Into<TableIdx>,
) -> Self {
Self::CallIndirectParamsImm16(CallIndirectParamsImm16 {
Self::CallIndirectParamsImm16(CallIndirectParams {
index: index.into(),
table: table.into(),
})
Expand Down
Loading

0 comments on commit 72be93b

Please sign in to comment.