Skip to content

Commit

Permalink
More support to dynamic field - Sui framework compilation (#73)
Browse files Browse the repository at this point in the history
* 061824-fixing-dynamic-fields: ini

* 061824-fixing-dynamic-fields: cleanup in module_context.rs

* 061824-fixing-dynamic-fields: dynamic-struct-example.move test added

* 061824-fixing-dynamic-fields: renaming actual to expected in test

* 062024-more-fixes-dynamic-struct: ini

* 062024-more-fixes-dynamic-struct: fix Type::get_bitwidth
  • Loading branch information
jcivlin authored Jun 21, 2024
1 parent f2a2eae commit 8390f8a
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 17 deletions.
13 changes: 13 additions & 0 deletions external-crates/move/crates/move-model/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2259,6 +2259,19 @@ impl<'env> StructEnv<'env> {
}
}
}

/// Printing fields of the structure to a String
pub fn print_to_string(&self) -> String {
let mut out = String::new();
let s_full_name = self.get_full_name_str();
out.push_str(&format!("Struct {}\n", s_full_name));
for (ii, field) in self.get_fields().enumerate() {
let f_name = field.get_name().display(self.symbol_pool()).to_string();
let ty = field.get_type();
out.push_str(&format!(" field {ii} name \'{}\' type {:#?}\n", f_name, ty));
}
out
}
}

// =================================================================================================
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
; ModuleID = '0x1__dynamic_structure'
source_filename = "tests/failure-tests/dynamic-struct-example.move"
target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128"
target triple = "sbf-solana-solana"

%__move_rt_type = type { { ptr, i64 }, i64, ptr }
%struct.dynamic_structure__Inner_u64.u64_ = type { i64, i64 }
%struct.dynamic_structure__DynamicStruct_dynamic_structure__Inner_u64.u64__ = type { i64, %struct.dynamic_structure__Inner_u64.u64_ }

@__move_rttydesc_signer = private unnamed_addr constant %__move_rt_type { { ptr, i64 } { ptr @__move_rttydesc_signer_name, i64 6 }, i64 9, ptr @__move_rttydesc_NOTHING_info }
@__move_rttydesc_signer_name = private unnamed_addr constant [6 x i8] c"signer"
@__move_rttydesc_NOTHING_info = private unnamed_addr constant i8 -1

declare i32 @memcmp(ptr, ptr, i64)

define void @"0000000000000001_dynamic_structu_unit_test_poiso_EmvQWiAB3EnqkA"() {
entry:
%local_0 = alloca i64, align 8
%local_1 = alloca { ptr, i64, i64 }, align 8
store i64 0, ptr %local_0, align 8
%loaded_alloca = load i64, ptr %local_0, align 8
%retval = call { ptr, i64, i64 } @move_native_unit_test_create_signers_for_testing(i64 %loaded_alloca)
store { ptr, i64, i64 } %retval, ptr %local_1, align 8
call void @move_rt_vec_destroy(ptr @__move_rttydesc_signer, ptr %local_1)
ret void
}

declare { ptr, i64, i64 } @move_native_unit_test_create_signers_for_testing(i64)

define void @"0000000000000001_dynamic_structu_test_fail_Fz3ntiKYooMYAv"() {
entry:
%local_0 = alloca i64, align 8
%local_1 = alloca i64, align 8
%local_2 = alloca i64, align 8
store i64 44, ptr %local_0, align 8
store i64 1, ptr %local_1, align 8
store i64 2, ptr %local_2, align 8
%call_arg_0 = load i64, ptr %local_0, align 8
%call_arg_1 = load i64, ptr %local_1, align 8
%call_arg_2 = load i64, ptr %local_2, align 8
call void @"0000000000000001_dynamic_structu_dynamic_struct_7V7jBxBPWFRsaZ"(i64 %call_arg_0, i64 %call_arg_1, i64 %call_arg_2)
ret void
}

define private void @"0000000000000001_dynamic_structu_dynamic_struct_7V7jBxBPWFRsaZ"(i64 %0, i64 %1, i64 %2) {
entry:
%local_0 = alloca i64, align 8
%local_1 = alloca i64, align 8
%local_2 = alloca i64, align 8
%local_3__id = alloca i64, align 8
%local_4__key = alloca i64, align 8
%local_5__val = alloca i64, align 8
%local_6__s = alloca %struct.dynamic_structure__Inner_u64.u64_, align 8
%local_7 = alloca %struct.dynamic_structure__DynamicStruct_dynamic_structure__Inner_u64.u64__, align 8
store i64 %0, ptr %local_0, align 8
store i64 %1, ptr %local_1, align 8
store i64 %2, ptr %local_2, align 8
%load_store_tmp = load i64, ptr %local_0, align 8
store i64 %load_store_tmp, ptr %local_3__id, align 8
%load_store_tmp1 = load i64, ptr %local_1, align 8
store i64 %load_store_tmp1, ptr %local_4__key, align 8
%load_store_tmp2 = load i64, ptr %local_2, align 8
store i64 %load_store_tmp2, ptr %local_5__val, align 8
%fv.0 = load i64, ptr %local_4__key, align 8
%fv.1 = load i64, ptr %local_5__val, align 8
%insert_0 = insertvalue %struct.dynamic_structure__Inner_u64.u64_ undef, i64 %fv.0, 0
%insert_1 = insertvalue %struct.dynamic_structure__Inner_u64.u64_ %insert_0, i64 %fv.1, 1
store %struct.dynamic_structure__Inner_u64.u64_ %insert_1, ptr %local_6__s, align 8
%fv.03 = load i64, ptr %local_3__id, align 8
%fv.14 = load %struct.dynamic_structure__Inner_u64.u64_, ptr %local_6__s, align 8
%insert_05 = insertvalue %struct.dynamic_structure__DynamicStruct_dynamic_structure__Inner_u64.u64__ undef, i64 %fv.03, 0
%insert_16 = insertvalue %struct.dynamic_structure__DynamicStruct_dynamic_structure__Inner_u64.u64__ %insert_05, %struct.dynamic_structure__Inner_u64.u64_ %fv.14, 1
store %struct.dynamic_structure__DynamicStruct_dynamic_structure__Inner_u64.u64__ %insert_16, ptr %local_7, align 8
ret void
}

declare void @move_rt_vec_destroy(ptr nonnull readonly dereferenceable(32), ptr)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
; ModuleID = '0x1__unit_test'
source_filename = "../../crates/move-stdlib/sources/unit_test.move"
target datalayout = "e-m:e-p:64:64-i64:64-n32:64-S128"
target triple = "sbf-solana-solana"

%__move_rt_type = type { { ptr, i64 }, i64, ptr }

@__move_rttydesc_signer = private unnamed_addr constant %__move_rt_type { { ptr, i64 } { ptr @__move_rttydesc_signer_name, i64 6 }, i64 9, ptr @__move_rttydesc_NOTHING_info }
@__move_rttydesc_signer_name = private unnamed_addr constant [6 x i8] c"signer"
@__move_rttydesc_NOTHING_info = private unnamed_addr constant i8 -1

declare i32 @memcmp(ptr, ptr, i64)

declare { ptr, i64, i64 } @move_native_unit_test_create_signers_for_testing(i64)

declare void @move_native_unit_test_poison()

define void @"0000000000000001_unit_test_unit_test_poiso_4afD3MScT99fc9"() {
entry:
%local_0 = alloca i64, align 8
%local_1 = alloca { ptr, i64, i64 }, align 8
store i64 0, ptr %local_0, align 8
%loaded_alloca = load i64, ptr %local_0, align 8
%retval = call { ptr, i64, i64 } @move_native_unit_test_create_signers_for_testing(i64 %loaded_alloca)
store { ptr, i64, i64 } %retval, ptr %local_1, align 8
call void @move_rt_vec_destroy(ptr @__move_rttydesc_signer, ptr %local_1)
ret void
}

declare void @move_rt_vec_destroy(ptr nonnull readonly dereferenceable(32), ptr)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module 0x1::dynamic_structure {
struct Inner<K, V> has copy, drop, store {
key: K,
val: V,
}

struct DynamicStruct<S: drop> has copy, drop, store {
id: u64,
s: S,
}

public fun dynamic_struct<K: drop, V: drop>(id: u64, key: K, val: V) {
DynamicStruct {
id,
s: Inner { key, val },
};
}
public fun test_fail() {
// If code is open then `dynamic_struct` is instantiated explicitly and
// `dynamic_struct` call below will not fail.
// DynamicStruct {
// id: 44,
// s: Inner { key: 1, val: 2 },
// };

// this was failing before commit `061824-fixing-dynamic-fields`
dynamic_struct(44, 1, 2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ impl<'mm, 'up> EntrypointGenerator<'mm, 'up> {
self.llvm_builder.build_cond_br(condition, then_bb, else_bb);
self.llvm_builder.position_at_end(then_bb);
let fn_name = fun.llvm_symbol_name(&[]);
debug!(target: "debug", "add_entries: function {fn_name}");
let fn_decls = self.fn_decls.borrow();
let ll_fun = fn_decls.get(&fn_name).unwrap();
let params = self.emit_entry_arguments(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ pub impl TypeExt for mty::Type {
Type::Reference(_, _) => 64,
Type::Vector(_) => 8 * MOVE_UNTYPED_VEC_DESC_SIZE,
Type::Struct(_m, _s, ref tys) => tys.iter().fold(0, |acc, ty| acc + ty.get_bitwidth()),
Type::Fun(v, _r) => {
let mut sz: u64 = 64; // Adding size of pointer in Box, it should be the same as for Reference
for i in 0..v.len() {
sz += v[i].get_bitwidth();
}
sz
}
Type::TypeParameter(_x) => 16,
_ => {
todo!("{self:?}")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ impl<'mm: 'up, 'up> ModuleContext<'mm, 'up> {
fn declare_structs(&mut self) {
use move_binary_format::{access::ModuleAccess, views::StructHandleView};
let m_env = &self.env;
let mod_name = &m_env.get_full_name_str();
let g_env = &m_env.env;

// Collect all the externally defined structures (transitively) used within this module.
Expand Down Expand Up @@ -135,7 +136,7 @@ impl<'mm: 'up, 'up> ModuleContext<'mm, 'up> {
all_structs.append(&mut local_structs);

debug!(target: "structs",
"Combined list of all structs{}",
"Module {mod_name} combined list of all structs{}",
self.dump_all_structs(&all_structs, false),
);

Expand Down Expand Up @@ -179,7 +180,10 @@ impl<'mm: 'up, 'up> ModuleContext<'mm, 'up> {
for s_def_inst in cm.struct_instantiations() {
let tys = m_env.get_type_actuals(Some(s_def_inst.type_parameters));
let s_env = m_env.get_struct_by_def_idx(s_def_inst.def);
let s_name = s_env.ll_struct_name_from_raw_name(&tys);
debug!(target: "structs", "Module {mod_name} trying to create opaque named structure for {s_name}, case struct");
if create_opaque_named_struct(&s_env, &tys) {
debug!(target: "structs", "Module {mod_name} created opaque named structure for {s_name}, case struct");
all_structs.push((s_env, tys));
}
}
Expand All @@ -189,7 +193,10 @@ impl<'mm: 'up, 'up> ModuleContext<'mm, 'up> {
let fld_handle = cm.field_handle_at(f_inst.handle);
let tys = m_env.get_type_actuals(Some(f_inst.type_parameters));
let s_env = m_env.get_struct_by_def_idx(fld_handle.owner);
let s_name = s_env.ll_struct_name_from_raw_name(&tys);
debug!(target: "structs", "Module {mod_name} trying to create opaque named structure for {s_name}, case field");
if create_opaque_named_struct(&s_env, &tys) {
debug!(target: "structs", "Module {mod_name} created opaque named structure for {s_name}, case field");
all_structs.push((s_env, tys));
}
}
Expand All @@ -214,7 +221,7 @@ impl<'mm: 'up, 'up> ModuleContext<'mm, 'up> {
}

debug!(target: "structs",
"Structs after visiting the signature table{}",
"Module {mod_name} structs after visiting the signature table{}",
self.dump_all_structs(&all_structs, false),
);

Expand All @@ -239,7 +246,7 @@ impl<'mm: 'up, 'up> ModuleContext<'mm, 'up> {

debug!(
target: "structs",
"Structs after translation{}",
"Module {mod_name} structs after translation{}",
self.dump_all_structs(&all_structs, true),
);
}
Expand All @@ -250,23 +257,30 @@ impl<'mm: 'up, 'up> ModuleContext<'mm, 'up> {
// are mixed in the nesting of type parameters,
// e.g. Struct_A<Vector<Struct_B<T>>>, where T is substituted by a
// concrete type, won't be declared correctly.
fn translate_struct(&self, s_env: &mm::StructEnv<'mm>, tyvec: &[mty::Type]) {
pub fn translate_struct(&self, s_env: &mm::StructEnv<'mm>, tyvec: &[mty::Type]) -> crate::stackless::StructType {
let ll_name = s_env.ll_struct_name_from_raw_name(tyvec);
debug!(target: "structs", "translating struct {}", s_env.struct_raw_type_name(tyvec));
debug!(target: "structs", "translating struct {}", ll_name);
let g_env = self.env.env;
// Visit each field in this struct, collecting field types.
let mut ll_field_tys = Vec::with_capacity(s_env.get_field_count() + 1);
for fld_env in s_env.get_fields() {
debug!(target: "structs", "translating field {:?}", &fld_env.get_type());
if let mty::Type::Struct(_m, _s, _tys) = &fld_env.get_type() {
if let mty::Type::Struct(mod_id, s_id, tys) = &fld_env.get_type() {
let mod_env = &g_env.get_module(*mod_id);
let struct_env = mod_env.get_struct(*s_id);
let struct_name = struct_env.ll_struct_name_from_raw_name(&tys);
debug!(target: "debug", "translate_struct: case 1 param is a structure {struct_name}");
let new_sty = &fld_env.get_type().instantiate(tyvec);
if let mty::Type::Struct(m, s, tys) = new_sty {
let g_env = &self.env.env;
let s_env = g_env.get_module(*m).into_struct(*s);
self.translate_struct(&s_env, tys);
}
} else if let mty::Type::TypeParameter(x) = &fld_env.get_type() {
if let mty::Type::Struct(m, s, tys) = &tyvec[*x as usize] {
let g_env = &self.env.env;
let mod_env = &g_env.get_module(*m);
let struct_env = mod_env.get_struct(*s);
let struct_name = struct_env.ll_struct_name_from_raw_name(&tys);
debug!(target: "debug", "translate_struct: case 2 param is a structure {struct_name}");
let s_env = g_env.get_module(*m).into_struct(*s);
self.translate_struct(&s_env, tys);
}
Expand All @@ -289,6 +303,10 @@ impl<'mm: 'up, 'up> ModuleContext<'mm, 'up> {
.named_struct_type(&ll_name)
.expect("no struct type");
ll_sty.set_struct_body(&ll_field_tys);
let dump = ll_sty.dump_to_string();
debug!(target: "debug", "Dumping translated structure {ll_name} {}", dump);
assert!(!self.llvm_cx.named_struct_type(&ll_name).is_none(), "At this point struct {} should already get some type", &ll_name);
ll_sty
}

// This method is used to declare structs found when function
Expand Down Expand Up @@ -681,31 +699,42 @@ impl<'mm: 'up, 'up> ModuleContext<'mm, 'up> {
None
}
}
Type::Struct(_mid, _sid, _tys) => {
Type::Struct(_mid, _sid, ttys) => {
// First substitute any generic type parameters occuring in _tys.
let new_sty = mty.instantiate(tyvec);
let mod_name = &self.env.get_full_name_str();
if Self::is_generic_struct(ttys) {
debug!(target: "debug", "Module {mod_name} generic struct {:#?}", new_sty);
}

debug!(
target: "structs",
"Instantiated struct {}",
"Instantiated struct {}, Type {:#?}",
new_sty
.get_struct(self.env.env)
.unwrap()
.0
.struct_raw_type_name(tyvec)
.struct_raw_type_name(tyvec),
new_sty
);
// Then process the (possibly type-substituted) struct.
if let Type::Struct(declaring_module_id, struct_id, tys) = new_sty {
let global_env = &self.env.env;
let struct_env = global_env
.get_module(declaring_module_id)
.into_struct(struct_id);
let decl_mod_name = global_env.get_module(declaring_module_id).get_full_name_str();
let struct_name = struct_env.ll_struct_name_from_raw_name(&tys);
debug!(target: "debug", "Module {decl_mod_name} structure {struct_name}");
if Self::is_generic_struct(&tys) {
debug!(target: "debug", "Module {decl_mod_name} generic struct {:#?}", struct_name);
}
if let Some(stype) = self.llvm_cx.named_struct_type(&struct_name) {
Some(stype.as_any_type())
} else {
debug!(target: "structs", "struct type for '{}' not found", &struct_name);
None
debug!(target: "structs", "struct type for '{}' not found, cretae it now", &struct_name);
let stype = self.translate_struct(&struct_env, &tys);
Some(stype.as_any_type())
}
} else {
unreachable!("")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,11 @@ impl<'mm, 'up> RttyContext<'mm, 'up> {
_ => unreachable!(),
};

debug!(target: "debug", "s_env {:#?}", &s_env);
if let Some(f_env) = &self.f_env {
let f_name = f_env.get_full_name_str();
debug!(target: "rtty", "f_name {:#?}", &f_name);
}


// Look up the corresponding LLVM struct type constructed earlier in the translation.
// Use it to collect field offsets, struct size, and struct alignment as computed by LLVM.
Expand Down
Loading

0 comments on commit 8390f8a

Please sign in to comment.