Skip to content

Optimize serialization of function calls #444

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

160 changes: 63 additions & 97 deletions src/hyperlight_common/src/flatbuffer_wrappers/function_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use alloc::string::{String, ToString};
use alloc::vec::Vec;

use anyhow::{bail, Error, Result};
use flatbuffers::{size_prefixed_root, WIPOffset};
use flatbuffers::{size_prefixed_root, FlatBufferBuilder, WIPOffset};
#[cfg(feature = "tracing")]
use tracing::{instrument, Span};

Expand All @@ -41,7 +41,6 @@ pub enum FunctionCallType {
}

/// `Functioncall` represents a call to a function in the guest or host.
#[derive(Clone)]
pub struct FunctionCall {
/// The function name
pub function_name: String,
Expand Down Expand Up @@ -72,91 +71,29 @@ impl FunctionCall {
pub fn function_call_type(&self) -> FunctionCallType {
self.function_call_type.clone()
}
}

#[cfg_attr(feature = "tracing", instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace"))]
pub fn validate_guest_function_call_buffer(function_call_buffer: &[u8]) -> Result<()> {
let guest_function_call_fb = size_prefixed_root::<FbFunctionCall>(function_call_buffer)
.map_err(|e| anyhow::anyhow!("Error reading function call buffer: {:?}", e))?;
match guest_function_call_fb.function_call_type() {
FbFunctionCallType::guest => Ok(()),
other => {
bail!("Invalid function call type: {:?}", other);
}
}
}

#[cfg_attr(feature = "tracing", instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace"))]
pub fn validate_host_function_call_buffer(function_call_buffer: &[u8]) -> Result<()> {
let host_function_call_fb = size_prefixed_root::<FbFunctionCall>(function_call_buffer)
.map_err(|e| anyhow::anyhow!("Error reading function call buffer: {:?}", e))?;
match host_function_call_fb.function_call_type() {
FbFunctionCallType::host => Ok(()),
other => {
bail!("Invalid function call type: {:?}", other);
}
}
}
/// Encodes self into the given builder and returns the encoded data.
pub fn encode<'a>(&self, builder: &'a mut FlatBufferBuilder) -> &'a [u8] {
let function_name = builder.create_string(&self.function_name);

impl TryFrom<&[u8]> for FunctionCall {
type Error = Error;
#[cfg_attr(feature = "tracing", instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace"))]
fn try_from(value: &[u8]) -> Result<Self> {
let function_call_fb = size_prefixed_root::<FbFunctionCall>(value)
.map_err(|e| anyhow::anyhow!("Error reading function call buffer: {:?}", e))?;
let function_name = function_call_fb.function_name();
let function_call_type = match function_call_fb.function_call_type() {
FbFunctionCallType::guest => FunctionCallType::Guest,
FbFunctionCallType::host => FunctionCallType::Host,
other => {
bail!("Invalid function call type: {:?}", other);
}
};
let expected_return_type = function_call_fb.expected_return_type().try_into()?;

let parameters = function_call_fb
.parameters()
.map(|v| {
v.iter()
.map(|p| p.try_into())
.collect::<Result<Vec<ParameterValue>>>()
})
.transpose()?;

Ok(Self {
function_name: function_name.to_string(),
parameters,
function_call_type,
expected_return_type,
})
}
}

impl TryFrom<FunctionCall> for Vec<u8> {
type Error = Error;
#[cfg_attr(feature = "tracing", instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace"))]
fn try_from(value: FunctionCall) -> Result<Vec<u8>> {
let mut builder = flatbuffers::FlatBufferBuilder::new();
let function_name = builder.create_string(&value.function_name);

let function_call_type = match value.function_call_type {
let function_call_type = match self.function_call_type {
FunctionCallType::Guest => FbFunctionCallType::guest,
FunctionCallType::Host => FbFunctionCallType::host,
};

let expected_return_type = value.expected_return_type.into();
let expected_return_type = self.expected_return_type.into();

let parameters = match &value.parameters {
let parameters = match &self.parameters {
Some(p) => {
let num_items = p.len();
let mut parameters: Vec<WIPOffset<Parameter>> = Vec::with_capacity(num_items);

for param in p {
match param {
ParameterValue::Int(i) => {
let hlint = hlint::create(&mut builder, &hlintArgs { value: *i });
let hlint = hlint::create(builder, &hlintArgs { value: *i });
let parameter = Parameter::create(
&mut builder,
builder,
&ParameterArgs {
value_type: FbParameterValue::hlint,
value: Some(hlint.as_union_value()),
Expand All @@ -165,9 +102,9 @@ impl TryFrom<FunctionCall> for Vec<u8> {
parameters.push(parameter);
}
ParameterValue::UInt(ui) => {
let hluint = hluint::create(&mut builder, &hluintArgs { value: *ui });
let hluint = hluint::create(builder, &hluintArgs { value: *ui });
let parameter = Parameter::create(
&mut builder,
builder,
&ParameterArgs {
value_type: FbParameterValue::hluint,
value: Some(hluint.as_union_value()),
Expand All @@ -176,9 +113,9 @@ impl TryFrom<FunctionCall> for Vec<u8> {
parameters.push(parameter);
}
ParameterValue::Long(l) => {
let hllong = hllong::create(&mut builder, &hllongArgs { value: *l });
let hllong = hllong::create(builder, &hllongArgs { value: *l });
let parameter = Parameter::create(
&mut builder,
builder,
&ParameterArgs {
value_type: FbParameterValue::hllong,
value: Some(hllong.as_union_value()),
Expand All @@ -187,10 +124,9 @@ impl TryFrom<FunctionCall> for Vec<u8> {
parameters.push(parameter);
}
ParameterValue::ULong(ul) => {
let hlulong =
hlulong::create(&mut builder, &hlulongArgs { value: *ul });
let hlulong = hlulong::create(builder, &hlulongArgs { value: *ul });
let parameter = Parameter::create(
&mut builder,
builder,
&ParameterArgs {
value_type: FbParameterValue::hlulong,
value: Some(hlulong.as_union_value()),
Expand All @@ -199,9 +135,9 @@ impl TryFrom<FunctionCall> for Vec<u8> {
parameters.push(parameter);
}
ParameterValue::Float(f) => {
let hlfloat = hlfloat::create(&mut builder, &hlfloatArgs { value: *f });
let hlfloat = hlfloat::create(builder, &hlfloatArgs { value: *f });
let parameter = Parameter::create(
&mut builder,
builder,
&ParameterArgs {
value_type: FbParameterValue::hlfloat,
value: Some(hlfloat.as_union_value()),
Expand All @@ -210,10 +146,9 @@ impl TryFrom<FunctionCall> for Vec<u8> {
parameters.push(parameter);
}
ParameterValue::Double(d) => {
let hldouble =
hldouble::create(&mut builder, &hldoubleArgs { value: *d });
let hldouble = hldouble::create(builder, &hldoubleArgs { value: *d });
let parameter = Parameter::create(
&mut builder,
builder,
&ParameterArgs {
value_type: FbParameterValue::hldouble,
value: Some(hldouble.as_union_value()),
Expand All @@ -223,9 +158,9 @@ impl TryFrom<FunctionCall> for Vec<u8> {
}
ParameterValue::Bool(b) => {
let hlbool: WIPOffset<hlbool<'_>> =
hlbool::create(&mut builder, &hlboolArgs { value: *b });
hlbool::create(builder, &hlboolArgs { value: *b });
let parameter = Parameter::create(
&mut builder,
builder,
&ParameterArgs {
value_type: FbParameterValue::hlbool,
value: Some(hlbool.as_union_value()),
Expand All @@ -236,10 +171,10 @@ impl TryFrom<FunctionCall> for Vec<u8> {
ParameterValue::String(s) => {
let hlstring = {
let val = builder.create_string(s.as_str());
hlstring::create(&mut builder, &hlstringArgs { value: Some(val) })
hlstring::create(builder, &hlstringArgs { value: Some(val) })
};
let parameter = Parameter::create(
&mut builder,
builder,
&ParameterArgs {
value_type: FbParameterValue::hlstring,
value: Some(hlstring.as_union_value()),
Expand All @@ -251,13 +186,13 @@ impl TryFrom<FunctionCall> for Vec<u8> {
let vec_bytes = builder.create_vector(v);

let hlvecbytes = hlvecbytes::create(
&mut builder,
builder,
&hlvecbytesArgs {
value: Some(vec_bytes),
},
);
let parameter = Parameter::create(
&mut builder,
builder,
&ParameterArgs {
value_type: FbParameterValue::hlvecbytes,
value: Some(hlvecbytes.as_union_value()),
Expand All @@ -279,7 +214,7 @@ impl TryFrom<FunctionCall> for Vec<u8> {
};

let function_call = FbFunctionCall::create(
&mut builder,
builder,
&FbFunctionCallArgs {
function_name: Some(function_name),
parameters,
Expand All @@ -288,9 +223,40 @@ impl TryFrom<FunctionCall> for Vec<u8> {
},
);
builder.finish_size_prefixed(function_call, None);
let res = builder.finished_data().to_vec();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This to_vec is the clone I was able to avoid, which is the most important part of this PR

builder.finished_data()
}
}

impl TryFrom<&[u8]> for FunctionCall {
type Error = Error;
#[cfg_attr(feature = "tracing", instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace"))]
fn try_from(value: &[u8]) -> Result<Self> {
let function_call_fb = size_prefixed_root::<FbFunctionCall>(value)
.map_err(|e| anyhow::anyhow!("Error reading function call buffer: {:?}", e))?;
let function_name = function_call_fb.function_name();
let function_call_type = match function_call_fb.function_call_type() {
FbFunctionCallType::guest => FunctionCallType::Guest,
FbFunctionCallType::host => FunctionCallType::Host,
other => {
bail!("Invalid function call type: {:?}", other);
}
};
let expected_return_type = function_call_fb.expected_return_type().try_into()?;
let parameters = function_call_fb
.parameters()
.map(|v| {
v.iter()
.map(|p| p.try_into())
.collect::<Result<Vec<ParameterValue>>>()
})
.transpose()?;

Ok(res)
Ok(Self {
function_name: function_name.to_string(),
parameters,
function_call_type,
expected_return_type,
})
}
}

Expand All @@ -303,7 +269,8 @@ mod tests {

#[test]
fn read_from_flatbuffer() -> Result<()> {
let test_data: Vec<u8> = FunctionCall::new(
let mut builder = FlatBufferBuilder::new();
let test_data = FunctionCall::new(
"PrintTwelveArgs".to_string(),
Some(vec![
ParameterValue::String("1".to_string()),
Expand All @@ -322,10 +289,9 @@ mod tests {
FunctionCallType::Guest,
ReturnType::Int,
)
.try_into()
.unwrap();
.encode(&mut builder);

let function_call = FunctionCall::try_from(test_data.as_slice())?;
let function_call = FunctionCall::try_from(test_data)?;
assert_eq!(function_call.function_name, "PrintTwelveArgs");
assert!(function_call.parameters.is_some());
let parameters = function_call.parameters.unwrap();
Expand Down
3 changes: 3 additions & 0 deletions src/hyperlight_common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ pub mod flatbuffer_wrappers;
mod flatbuffers;
/// cbindgen:ignore
pub mod mem;

extern crate flatbuffers as fb;
pub use fb::FlatBufferBuilder;
2 changes: 1 addition & 1 deletion src/hyperlight_guest/src/guest_function_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ fn internal_dispatch_function() -> Result<()> {
set_error(e.kind.clone(), e.message.as_str());
})?;

push_shared_output_data(result_vec)
push_shared_output_data(&result_vec)
}

// This is implemented as a separate function to make sure that epilogue in the internal_dispatch_function is called before the halt()
Expand Down
8 changes: 3 additions & 5 deletions src/hyperlight_guest/src/host_function_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result;
use hyperlight_common::mem::RunMode;
use hyperlight_common::FlatBufferBuilder;

use crate::error::{HyperlightGuestError, Result};
use crate::host_error::check_for_host_error;
Expand Down Expand Up @@ -73,11 +74,8 @@ pub fn call_host_function(

validate_host_function_call(&host_function_call)?;

let host_function_call_buffer: Vec<u8> = host_function_call
.try_into()
.expect("Unable to serialize host function call");

push_shared_output_data(host_function_call_buffer)?;
let mut builder = FlatBufferBuilder::new();
push_shared_output_data(host_function_call.encode(&mut builder))?;

outb(OutBAction::CallFunction as u16, 0);

Expand Down
2 changes: 1 addition & 1 deletion src/hyperlight_guest/src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn write_log_data(
.try_into()
.expect("Failed to convert GuestLogData to bytes");

push_shared_output_data(bytes).expect("Unable to push log data to shared output data");
push_shared_output_data(&bytes).expect("Unable to push log data to shared output data");
}

pub fn log_message(
Expand Down
3 changes: 1 addition & 2 deletions src/hyperlight_guest/src/shared_output_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ limitations under the License.

use alloc::format;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::slice::from_raw_parts_mut;

use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;

use crate::error::{HyperlightGuestError, Result};
use crate::P_PEB;

pub fn push_shared_output_data(data: Vec<u8>) -> Result<()> {
pub fn push_shared_output_data(data: &[u8]) -> Result<()> {
let peb_ptr = unsafe { P_PEB.unwrap() };
let shared_buffer_size = unsafe { (*peb_ptr).outputdata.outputDataSize as usize };
let odb = unsafe {
Expand Down
1 change: 0 additions & 1 deletion src/hyperlight_host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ rand = { version = "0.9" }
cfg-if = { version = "1.0.0" }
libc = { version = "0.2.172" }
paste = "1.0"
flatbuffers = "25.2.10"
page_size = "0.6.0"
termcolor = "1.2.0"
bitflags = "2.9.0"
Expand Down
Loading
Loading