Skip to content

Commit

Permalink
Switch to vector bytecode instructions (#37)
Browse files Browse the repository at this point in the history
* [move-compiler][move-stdlib] Switch to vector bytecode instructions

- Add annotation for native functions that are bytecode instructions
- Add compiler support for swapping those annotated functions to known bytecode instructions

* fixup! [move-compiler][move-stdlib] Switch to vector bytecode instructions

* fixup! fixup! [move-compiler][move-stdlib] Switch to vector bytecode instructions

* fixup! fixup! fixup! [move-compiler][move-stdlib] Switch to vector bytecode instructions

* fixup! fixup! fixup! fixup! [move-compiler][move-stdlib] Switch to vector bytecode instructions

Signed-off-by: sahithiacn <[email protected]>
Closes: #325
  • Loading branch information
tnowacki authored and bors-diem committed Jan 11, 2023
1 parent b2760c7 commit a6b92d4
Show file tree
Hide file tree
Showing 19 changed files with 455 additions and 83 deletions.
30 changes: 14 additions & 16 deletions language/extensions/async/move-async-vm/src/natives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,20 @@ pub struct AsyncExtension {
pub fn actor_natives(
async_addr: AccountAddress,
) -> Vec<(AccountAddress, Identifier, Identifier, NativeFunction)> {
native_functions::make_table(
async_addr,
&[
("Actor", "self", native_self),
("Actor", "virtual_time", native_virtual_time),
("Runtime", "send__0", native_send),
("Runtime", "send__1", native_send),
("Runtime", "send__2", native_send),
("Runtime", "send__3", native_send),
("Runtime", "send__4", native_send),
("Runtime", "send__5", native_send),
("Runtime", "send__6", native_send),
("Runtime", "send__7", native_send),
("Runtime", "send__8", native_send),
],
)
const NATIVES: &[(&str, &str, NativeFunction)] = &[
("Actor", "self", native_self),
("Actor", "virtual_time", native_virtual_time),
("Runtime", "send__0", native_send),
("Runtime", "send__1", native_send),
("Runtime", "send__2", native_send),
("Runtime", "send__3", native_send),
("Runtime", "send__4", native_send),
("Runtime", "send__5", native_send),
("Runtime", "send__6", native_send),
("Runtime", "send__7", native_send),
("Runtime", "send__8", native_send),
];
native_functions::make_table(async_addr, NATIVES)
}

fn native_self(
Expand Down
2 changes: 2 additions & 0 deletions language/move-compiler/src/diagnostics/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ codes!(
InvalidValue: { msg: "invalid attribute value", severity: NonblockingError },
InvalidUsage: { msg: "invalid usage of known attribute", severity: NonblockingError },
InvalidTest: { msg: "unable to generate test", severity: NonblockingError },
InvalidBytecodeInst:
{ msg: "unknown bytecode instruction function", severity: NonblockingError },
],
Tests: [
TestFailed: { msg: "test failure", severity: BlockingError },
Expand Down
127 changes: 127 additions & 0 deletions language/move-compiler/src/naming/fake_natives.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! This module verifies the usage of the "fake native" functions. These functions are declared
//! as 'native`, but do not appear in the compiled module. For developer sanity, they must be marked
//! with the `FAKE_NATIVE_ATTR`
use std::convert::TryInto;

use crate::{
diag,
expansion::ast::{Address, AttributeName_, ModuleIdent, ModuleIdent_},
naming::ast as N,
parser::ast::FunctionName,
shared::{
known_attributes::{KnownAttribute, NativeAttribute},
CompilationEnv, Identifier,
},
};
use move_ir_types::ast as IR;

pub const FAKE_NATIVE_ATTR: AttributeName_ =
AttributeName_::Known(KnownAttribute::Native(NativeAttribute::BytecodeInstruction));

/// verify `FAKE_NATIVE_ATTR` usage
pub fn function(
env: &mut CompilationEnv,
module_opt: Option<ModuleIdent>,
function_name: FunctionName,
function: &N::Function,
) {
let loc = match function.attributes.get_loc_(&FAKE_NATIVE_ATTR) {
None => return,
Some(loc) => *loc,
};
let module = match module_opt {
Some(module) => module,
None => {
let msg = format!(
"Invalid usage of '{}' attribute to map function to bytecode instruction.",
NativeAttribute::BYTECODE_INSTRUCTION
);
let smsg = "Script functions are never mapped to bytecode instructions";
let diag = diag!(
Attributes::InvalidBytecodeInst,
(loc, msg),
(function_name.loc(), smsg),
);
env.add_diag(diag);
return;
}
};
if resolve_builtin(&module, &function_name).is_none() {
let attr_msg = format!(
"Invalid usage of '{}' attribute to map function to bytecode instruction.",
NativeAttribute::BYTECODE_INSTRUCTION
);
let name_msg = format!(
"No known mapping of '{}::{}' to bytecode instruction",
module, function_name
);
let diag = diag!(
Attributes::InvalidBytecodeInst,
(loc, attr_msg),
(function_name.loc(), name_msg),
);
env.add_diag(diag);
}
match &function.body.value {
N::FunctionBody_::Native => (),
N::FunctionBody_::Defined(_) => {
let attr_msg = format!(
"Invalid usage of '{}' attribute on non-native function",
NativeAttribute::BYTECODE_INSTRUCTION
);
let diag = diag!(Attributes::InvalidBytecodeInst, (loc, attr_msg));
env.add_diag(diag);
}
}
}

/// Resolve the mapping for a module + function name to a bytecode instruction.
/// The function should already be verified by `function` above
pub fn resolve_builtin(
module: &ModuleIdent,
function: &FunctionName,
) -> Option<fn(Vec<IR::Type>) -> IR::Bytecode_> {
let sp!(_, ModuleIdent_ { address, module }) = module;
let addr_name = match address {
Address::Numerical(Some(sp!(_, n_)), _) | Address::NamedUnassigned(sp!(_, n_)) => n_,
_ => return None,
};
Some(
match (
addr_name.as_str(),
module.value().as_str(),
function.value().as_str(),
) {
("Std", "Vector", "empty") => |tys| IR::Bytecode_::VecPack(expect_one_ty_arg(tys), 0),
("Std", "Vector", "length") => |tys| IR::Bytecode_::VecLen(expect_one_ty_arg(tys)),
("Std", "Vector", "borrow") => {
|tys| IR::Bytecode_::VecImmBorrow(expect_one_ty_arg(tys))
}
("Std", "Vector", "push_back") => {
|tys| IR::Bytecode_::VecPushBack(expect_one_ty_arg(tys))
}
("Std", "Vector", "borrow_mut") => {
|tys| IR::Bytecode_::VecMutBorrow(expect_one_ty_arg(tys))
}
("Std", "Vector", "pop_back") => {
|tys| IR::Bytecode_::VecPopBack(expect_one_ty_arg(tys))
}
("Std", "Vector", "destroy_empty") => {
|tys| IR::Bytecode_::VecUnpack(expect_one_ty_arg(tys), 0)
}
("Std", "Vector", "swap") => |tys| IR::Bytecode_::VecSwap(expect_one_ty_arg(tys)),
_ => return None,
},
)
}

fn expect_one_ty_arg(ty_args: Vec<IR::Type>) -> IR::Type {
let [ty]: [IR::Type; 1] = ty_args
.try_into()
.expect("ICE native bytecode function expected a single type argument");
ty
}
1 change: 1 addition & 0 deletions language/move-compiler/src/naming/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
// SPDX-License-Identifier: Apache-2.0

pub mod ast;
pub(crate) mod fake_natives;
pub(crate) mod translate;
36 changes: 26 additions & 10 deletions language/move-compiler/src/naming/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use move_ir_types::location::*;
use move_symbol_pool::Symbol;
use std::collections::BTreeMap;

use super::fake_natives;

//**************************************************************************************************
// Context
//**************************************************************************************************
Expand Down Expand Up @@ -377,7 +379,7 @@ fn module(
});
let functions = efunctions.map(|name, f| {
context.restore_unscoped(unscoped.clone());
function(context, name, f)
function(context, Some(ident), name, f)
});
let constants = econstants.map(|name, c| {
context.restore_unscoped(unscoped.clone());
Expand Down Expand Up @@ -428,7 +430,7 @@ fn script(context: &mut Context, escript: E::Script) -> N::Script {
constant(context, name, c)
});
context.restore_unscoped(inner_unscoped);
let function = function(context, function_name, efunction);
let function = function(context, None, function_name, efunction);
context.restore_unscoped(outer_unscoped);
N::Script {
package_name,
Expand Down Expand Up @@ -476,19 +478,33 @@ fn friend(context: &mut Context, mident: ModuleIdent, friend: E::Friend) -> Opti
// Functions
//**************************************************************************************************

fn function(context: &mut Context, _name: FunctionName, f: E::Function) -> N::Function {
let attributes = f.attributes;
let visibility = f.visibility;
let signature = function_signature(context, f.signature);
let acquires = function_acquires(context, f.acquires);
let body = function_body(context, f.body);
N::Function {
fn function(
context: &mut Context,
module_opt: Option<ModuleIdent>,
name: FunctionName,
ef: E::Function,
) -> N::Function {
let E::Function {
attributes,
loc: _,
visibility,
signature,
acquires,
body,
}
specs: _,
} = ef;
let signature = function_signature(context, signature);
let acquires = function_acquires(context, acquires);
let body = function_body(context, body);
let f = N::Function {
attributes,
visibility,
signature,
acquires,
body,
};
fake_natives::function(&mut context.env, module_opt, name, &f);
f
}

fn function_signature(context: &mut Context, sig: E::FunctionSignature) -> N::FunctionSignature {
Expand Down
30 changes: 30 additions & 0 deletions language/move-compiler/src/shared/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ pub mod known_attributes {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum KnownAttribute {
Testing(TestingAttribute),
Native(NativeAttribute),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
Expand All @@ -555,6 +556,12 @@ pub mod known_attributes {
ExpectedFailure,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum NativeAttribute {
// It is a fake native function that actually compiles to a bytecode instruction
BytecodeInstruction,
}

impl fmt::Display for AttributePosition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Expand All @@ -579,19 +586,24 @@ pub mod known_attributes {
TestingAttribute::EXPECTED_FAILURE => {
Self::Testing(TestingAttribute::ExpectedFailure)
}
NativeAttribute::BYTECODE_INSTRUCTION => {
Self::Native(NativeAttribute::BytecodeInstruction)
}
_ => return None,
})
}

pub const fn name(&self) -> &str {
match self {
Self::Testing(a) => a.name(),
Self::Native(a) => a.name(),
}
}

pub fn expected_positions(&self) -> &'static BTreeSet<AttributePosition> {
match self {
Self::Testing(a) => a.expected_positions(),
Self::Native(a) => a.expected_positions(),
}
}
}
Expand Down Expand Up @@ -634,4 +646,22 @@ pub mod known_attributes {
}
}
}

impl NativeAttribute {
pub const BYTECODE_INSTRUCTION: &'static str = "bytecode_instruction";

pub const fn name(&self) -> &str {
match self {
NativeAttribute::BytecodeInstruction => Self::BYTECODE_INSTRUCTION,
}
}

pub fn expected_positions(&self) -> &'static BTreeSet<AttributePosition> {
static BYTECODE_INSTRUCTION_POSITIONS: Lazy<BTreeSet<AttributePosition>> =
Lazy::new(|| IntoIterator::into_iter([AttributePosition::Function]).collect());
match self {
NativeAttribute::BytecodeInstruction => &*BYTECODE_INSTRUCTION_POSITIONS,
}
}
}
}
51 changes: 42 additions & 9 deletions language/move-compiler/src/to_bytecode/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use crate::{
ast::{self as H, Value_},
translate::{display_var, DisplayVar},
},
naming::ast::{BuiltinTypeName_, StructTypeParameter, TParam},
naming::{
ast::{BuiltinTypeName_, StructTypeParameter, TParam},
fake_natives,
},
parser::ast::{
Ability, Ability_, BinOp, BinOp_, ConstantName, Field, FunctionName, StructName, UnaryOp,
UnaryOp_, Var, Visibility,
Expand Down Expand Up @@ -82,12 +85,24 @@ fn extract_decls(
let context = &mut Context::new(compilation_env, None);
let fdecls = all_modules()
.flat_map(|(m, mdef)| {
mdef.functions.key_cloned_iter().map(move |(f, fdef)| {
let key = (m, f);
let seen = seen_structs(&fdef.signature);
let gsig = fdef.signature.clone();
(key, (seen, gsig))
})
mdef.functions
.key_cloned_iter()
// TODO full prover support for vector bytecode instructions
// TODO filter out fake natives
// These cannot be filtered out due to lacking prover support for the operations
// .filter(|(_, fdef)| {
// // TODO full evm support for vector bytecode instructions
// cfg!(feature = "evm-backend")
// || !fdef
// .attributes
// .contains_key_(&fake_natives::FAKE_NATIVE_ATTR)
// })
.map(move |(f, fdef)| {
let key = (m, f);
let seen = seen_structs(&fdef.signature);
let gsig = fdef.signature.clone();
(key, (seen, gsig))
})
})
.map(|(key, (seen, gsig))| (key, (seen, function_signature(context, gsig))))
.collect();
Expand Down Expand Up @@ -177,6 +192,16 @@ fn module(
let functions = mdef
.functions
.into_iter()
// TODO full prover support for vector bytecode instructions
// TODO filter out fake natives
// These cannot be filtered out due to lacking prover support for the operations
// .filter(|(_, fdef)| {
// // TODO full evm support for vector bytecode instructions
// cfg!(feature = "evm-backend")
// || !fdef
// .attributes
// .contains_key_(&fake_natives::FAKE_NATIVE_ATTR)
// })
.map(|(f, fdef)| {
let (res, info) = function(&mut context, Some(&ident), f, fdef);
collected_function_infos.add(f, info).unwrap();
Expand Down Expand Up @@ -1073,8 +1098,16 @@ fn module_call(
tys: Vec<H::BaseType>,
) {
use IR::Bytecode_ as B;
let (m, n) = context.qualified_function_name(&mident, fname);
code.push(sp(loc, B::Call(m, n, base_types(context, tys))))
match fake_natives::resolve_builtin(&mident, &fname) {
// TODO full evm support for vector bytecode instructions
Some(mk_bytecode) if !cfg!(feature = "evm-backend") => {
code.push(sp(loc, mk_bytecode(base_types(context, tys))))
}
_ => {
let (m, n) = context.qualified_function_name(&mident, fname);
code.push(sp(loc, B::Call(m, n, base_types(context, tys))))
}
}
}

fn builtin(context: &mut Context, code: &mut IR::BytecodeBlock, sp!(loc, b_): H::BuiltinFunction) {
Expand Down
Loading

0 comments on commit a6b92d4

Please sign in to comment.