Skip to content

Commit

Permalink
feat: schema lazy eval for config attr (#797)
Browse files Browse the repository at this point in the history
* feat: support schema override attribute lazy eval and reduce unnecessary calculations and improve performance.

Signed-off-by: peefy <[email protected]>

* feat: add without insert index check for dict attributes

Signed-off-by: peefy <[email protected]>

---------

Signed-off-by: peefy <[email protected]>
  • Loading branch information
Peefy authored Oct 23, 2023
1 parent d28ea21 commit 2b751aa
Show file tree
Hide file tree
Showing 13 changed files with 152 additions and 33 deletions.
90 changes: 69 additions & 21 deletions kclvm/compiler/src/codegen/llvm/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1391,10 +1391,6 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> {
self.walk_decorator_with_name(&decorator.node, Some(name), false)
.expect(kcl_error::COMPILE_ERROR_MSG);
}
let value = match &schema_attr.value {
Some(value) => self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG),
None => self.undefined_value(),
};
let config_value = self
.get_variable(value::SCHEMA_CONFIG_NAME)
.expect(kcl_error::INTERNAL_ERROR_MSG);
Expand All @@ -1409,22 +1405,6 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> {
&ApiFunc::kclvm_config_attr_map.name(),
&[schema_value, string_ptr_value, type_str_ptr_value],
);
if let Some(op) = &schema_attr.op {
match op {
// Union
ast::BinOrAugOp::Aug(ast::AugOp::BitOr) => {
let org_value = self.build_call(
&ApiFunc::kclvm_dict_get_value.name(),
&[schema_value, string_ptr_value],
);
let fn_name = ApiFunc::kclvm_value_op_bit_or;
let value = self.build_call(&fn_name.name(), &[org_value, value]);
self.dict_merge(schema_value, name, value, 1, -1);
}
// Assign
_ => self.dict_merge(schema_value, name, value, 1, -1),
}
}
let has_key = self
.build_call(
&ApiFunc::kclvm_dict_has_value.name(),
Expand All @@ -1436,13 +1416,58 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> {
.build_int_compare(IntPredicate::NE, has_key, self.native_i8_zero(), "");
let then_block = self.append_block("");
let else_block = self.append_block("");
let end_block = self.append_block("");
self.builder
.build_conditional_branch(has_key, then_block, else_block);
self.builder.position_at_end(then_block);
let config_attr_value = self.build_call(
&ApiFunc::kclvm_dict_get_entry.name(),
&[config_value, string_ptr_value],
);
// If the attribute operator is not `=`, eval the schema attribute value.
// if is_not_override:
let is_override_attr = self
.build_call(
&ApiFunc::kclvm_dict_is_override_attr.name(),
&[config_value, string_ptr_value],
)
.into_int_value();
let is_not_override_attr = self.builder.build_int_compare(
IntPredicate::EQ,
is_override_attr,
self.native_i8_zero(),
"",
);
let is_not_override_then_block = self.append_block("");
let is_not_override_else_block = self.append_block("");
self.builder.build_conditional_branch(
is_not_override_attr,
is_not_override_then_block,
is_not_override_else_block,
);
self.builder.position_at_end(is_not_override_then_block);
let value = match &schema_attr.value {
Some(value) => self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG),
None => self.undefined_value(),
};
if let Some(op) = &schema_attr.op {
match op {
// Union
ast::BinOrAugOp::Aug(ast::AugOp::BitOr) => {
let org_value = self.build_call(
&ApiFunc::kclvm_dict_get_value.name(),
&[schema_value, string_ptr_value],
);
let fn_name = ApiFunc::kclvm_value_op_bit_or;
let value = self.build_call(&fn_name.name(), &[org_value, value]);
self.dict_merge(schema_value, name, value, 1, -1);
}
// Assign
_ => self.dict_merge(schema_value, name, value, 1, -1),
}
}
self.br(is_not_override_else_block);
self.builder.position_at_end(is_not_override_else_block);
self.value_union(schema_value, config_attr_value);
let cal_map = self
.get_variable(value::SCHEMA_CAL_MAP)
Expand Down Expand Up @@ -1476,8 +1501,31 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> {
}
}
}
self.br(else_block);
self.br(end_block);
self.builder.position_at_end(else_block);
// Lazy eval for the schema attribute.
let value = match &schema_attr.value {
Some(value) => self.walk_expr(value).expect(kcl_error::COMPILE_ERROR_MSG),
None => self.undefined_value(),
};
if let Some(op) = &schema_attr.op {
match op {
// Union
ast::BinOrAugOp::Aug(ast::AugOp::BitOr) => {
let org_value = self.build_call(
&ApiFunc::kclvm_dict_get_value.name(),
&[schema_value, string_ptr_value],
);
let fn_name = ApiFunc::kclvm_value_op_bit_or;
let value = self.build_call(&fn_name.name(), &[org_value, value]);
self.dict_merge(schema_value, name, value, 1, -1);
}
// Assign
_ => self.dict_merge(schema_value, name, value, 1, -1),
}
}
self.br(end_block);
self.builder.position_at_end(end_block);
Ok(schema_value)
}

Expand Down
Binary file modified kclvm/runtime/src/_kclvm.bc
Binary file not shown.
6 changes: 4 additions & 2 deletions kclvm/runtime/src/_kclvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,8 @@ void kclvm_dict_insert_unpack(kclvm_value_ref_t* p, kclvm_value_ref_t* v);

void kclvm_dict_insert_value(kclvm_value_ref_t* p, kclvm_value_ref_t* key, kclvm_value_ref_t* v, kclvm_size_t op, kclvm_size_t insert_index);

kclvm_bool_t kclvm_dict_is_override_attr(kclvm_value_ref_t* p, kclvm_char_t* key);

kclvm_value_ref_t* kclvm_dict_keys(kclvm_value_ref_t* p);

kclvm_size_t kclvm_dict_len(kclvm_value_ref_t* p);
Expand Down Expand Up @@ -336,7 +338,7 @@ kclvm_value_ref_t* kclvm_iterator_next_value(kclvm_iterator_t* p, kclvm_value_re

kclvm_value_ref_t* kclvm_json_decode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs);

kclvm_value_ref_t* kclvm_json_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs);
kclvm_value_ref_t* kclvm_json_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs);

kclvm_value_ref_t* kclvm_json_encode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs);

Expand Down Expand Up @@ -702,7 +704,7 @@ kclvm_value_ref_t* kclvm_value_union_all(kclvm_context_t* _ctx, kclvm_value_ref_

kclvm_value_ref_t* kclvm_yaml_decode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs);

kclvm_value_ref_t* kclvm_yaml_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs);
kclvm_value_ref_t* kclvm_yaml_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs);

kclvm_value_ref_t* kclvm_yaml_encode(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs);

Expand Down
10 changes: 4 additions & 6 deletions kclvm/runtime/src/_kclvm.ll
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ declare void @kclvm_dict_insert_unpack(%kclvm_value_ref_t* %p, %kclvm_value_ref_

declare void @kclvm_dict_insert_value(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %key, %kclvm_value_ref_t* %v, %kclvm_size_t %op, %kclvm_size_t %insert_index);

declare %kclvm_bool_t @kclvm_dict_is_override_attr(%kclvm_value_ref_t* %p, %kclvm_char_t* %key);

declare %kclvm_value_ref_t* @kclvm_dict_keys(%kclvm_value_ref_t* %p);

declare %kclvm_size_t @kclvm_dict_len(%kclvm_value_ref_t* %p);
Expand Down Expand Up @@ -284,7 +286,7 @@ declare %kclvm_value_ref_t* @kclvm_iterator_next_value(%kclvm_iterator_t* %p, %k

declare %kclvm_value_ref_t* @kclvm_json_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs);

declare %kclvm_value_ref_t* @kclvm_json_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs);
declare %kclvm_value_ref_t* @kclvm_json_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs);

declare %kclvm_value_ref_t* @kclvm_json_encode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs);

Expand Down Expand Up @@ -430,10 +432,6 @@ declare %kclvm_value_ref_t* @kclvm_schema_value_new(%kclvm_context_t* %ctx, %kcl

declare %kclvm_size_t @kclvm_strlen(i8* %ptr);

declare void @kclvm_testing_arguments(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs);

declare void @kclvm_testing_setting_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %_args, %kclvm_value_ref_t* %_kwargs);

declare %kclvm_value_ref_t* @kclvm_units_to_G(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs);

declare %kclvm_value_ref_t* @kclvm_units_to_Gi(%kclvm_context_t* %ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs);
Expand Down Expand Up @@ -654,7 +652,7 @@ declare %kclvm_value_ref_t* @kclvm_value_union_all(%kclvm_context_t* %_ctx, %kcl

declare %kclvm_value_ref_t* @kclvm_yaml_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs);

declare %kclvm_value_ref_t* @kclvm_yaml_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs);
declare %kclvm_value_ref_t* @kclvm_yaml_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs);

declare %kclvm_value_ref_t* @kclvm_yaml_encode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs);

Expand Down
1 change: 1 addition & 0 deletions kclvm/runtime/src/_kclvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ pub enum ApiFunc {
kclvm_dict_insert,
kclvm_dict_insert_unpack,
kclvm_dict_insert_value,
kclvm_dict_is_override_attr,
kclvm_dict_keys,
kclvm_dict_len,
kclvm_dict_merge,
Expand Down
1 change: 1 addition & 0 deletions kclvm/runtime/src/_kclvm_addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ pub fn _kclvm_get_fn_ptr_by_name(name: &str) -> u64 {
"kclvm_dict_insert" => crate::kclvm_dict_insert as *const () as u64,
"kclvm_dict_insert_unpack" => crate::kclvm_dict_insert_unpack as *const () as u64,
"kclvm_dict_insert_value" => crate::kclvm_dict_insert_value as *const () as u64,
"kclvm_dict_is_override_attr" => crate::kclvm_dict_is_override_attr as *const () as u64,
"kclvm_dict_keys" => crate::kclvm_dict_keys as *const () as u64,
"kclvm_dict_len" => crate::kclvm_dict_len as *const () as u64,
"kclvm_dict_merge" => crate::kclvm_dict_merge as *const () as u64,
Expand Down
12 changes: 8 additions & 4 deletions kclvm/runtime/src/_kclvm_api_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@
// api-spec(c): void kclvm_dict_clear(kclvm_value_ref_t* p);
// api-spec(llvm): declare void @kclvm_dict_clear(%kclvm_value_ref_t* %p);

// api-spec: kclvm_dict_is_override_attr
// api-spec(c): kclvm_bool_t kclvm_dict_is_override_attr(kclvm_value_ref_t* p, kclvm_char_t* key);
// api-spec(llvm): declare %kclvm_bool_t @kclvm_dict_is_override_attr(%kclvm_value_ref_t* %p, %kclvm_char_t* %key);

// api-spec: kclvm_dict_get
// api-spec(c): kclvm_value_ref_t* kclvm_dict_get(kclvm_value_ref_t* p, kclvm_value_ref_t* key);
// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_dict_get(%kclvm_value_ref_t* %p, %kclvm_value_ref_t* %key);
Expand Down Expand Up @@ -899,8 +903,8 @@
// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_json_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs);

// api-spec: kclvm_json_dump_to_file
// api-spec(c): kclvm_value_ref_t* kclvm_json_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs);
// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_json_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs);
// api-spec(c): kclvm_value_ref_t* kclvm_json_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs);
// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_json_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs);

// api-spec: kclvm_manifests_yaml_stream
// api-spec(c): void kclvm_manifests_yaml_stream(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs);
Expand Down Expand Up @@ -1251,6 +1255,6 @@
// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_yaml_decode(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs);

// api-spec: kclvm_yaml_dump_to_file
// api-spec(c): kclvm_value_ref_t* kclvm_yaml_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs);
// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_yaml_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %_kwargs);
// api-spec(c): kclvm_value_ref_t* kclvm_yaml_dump_to_file(kclvm_context_t* _ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* kwargs);
// api-spec(llvm): declare %kclvm_value_ref_t* @kclvm_yaml_dump_to_file(%kclvm_context_t* %_ctx, %kclvm_value_ref_t* %args, %kclvm_value_ref_t* %kwargs);

16 changes: 16 additions & 0 deletions kclvm/runtime/src/value/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,22 @@ pub unsafe extern "C" fn kclvm_dict_clear(p: *mut kclvm_value_ref_t) {
p.dict_clear();
}

#[no_mangle]
#[runtime_fn]
pub unsafe extern "C" fn kclvm_dict_is_override_attr(
p: *const kclvm_value_ref_t,
key: *const kclvm_char_t,
) -> kclvm_bool_t {
let p = ptr_as_ref(p);
let key = c2str(key);
let is_override_op = matches!(
p.dict_get_attr_operator(key),
Some(ConfigEntryOperationKind::Override)
);
let without_index = matches!(p.dict_get_insert_index(key), Some(-1) | None);
(is_override_op && without_index) as kclvm_bool_t
}

#[no_mangle]
#[runtime_fn]
pub unsafe extern "C" fn kclvm_dict_get(
Expand Down
20 changes: 20 additions & 0 deletions kclvm/runtime/src/value/val_dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,26 @@ impl ValueRef {
}
}

/// Dict get value e.g., {k1 = v1, k2 = v2}.get_attr_operator(k1) == Some(ConfigEntryOperationKind::Override)
pub fn dict_get_attr_operator(&self, key: &str) -> Option<ConfigEntryOperationKind> {
match &*self.rc.borrow() {
Value::dict_value(ref dict) => dict.ops.get(key).cloned(),
Value::schema_value(ref schema) => schema.config.ops.get(key).cloned(),
_ => None,
}
}

/// Dict get value e.g., {k1 = v1, k2 = v2}.get_attr_operator(k1) == Some(ConfigEntryOperationKind::Override)
pub fn dict_get_insert_index(&self, key: &str) -> Option<i32> {
match &*self.rc.borrow() {
Value::dict_value(ref dict) => Some(*dict.insert_indexs.get(key).unwrap_or(&-1)),
Value::schema_value(ref schema) => {
Some(*schema.config.insert_indexs.get(key).unwrap_or(&-1))
}
_ => None,
}
}

/// Dict get entry e.g., {k1: v1, k2, v2}.get_entry(k1) == {k1: v1}
pub fn dict_get_entry(&self, key: &str) -> Option<ValueRef> {
match &*self.rc.borrow() {
Expand Down
10 changes: 10 additions & 0 deletions test/grammar/schema/config_op/override/override_3/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
schema MySubSchema:
a: int
b: str
c: {str:str}
d: str = c[b]

schema MyTestSchema:
field1?: [MySubSchema] = [{a = 1, b = "key", c = {key = "value"}}]

instance = MyTestSchema {field1: None}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
instance:
field1:
- a: 1
b: key
c:
key: value
d: value
10 changes: 10 additions & 0 deletions test/grammar/schema/config_op/override/override_4/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
schema MySubSchema:
a: int
b: str
c: {str:str}
d: str = c[b]

schema MyTestSchema:
field1?: [MySubSchema] = [{a: 1}]

instance = MyTestSchema {field1 = None}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
instance:
field1: null

0 comments on commit 2b751aa

Please sign in to comment.