From 70b36250436cc17b41c08ee346820cfc72dffe58 Mon Sep 17 00:00:00 2001 From: peefy Date: Mon, 23 Oct 2023 13:52:35 +0800 Subject: [PATCH] feat: support schema override attribute lazy eval and reduce unnecessary calculations and improve performance. Signed-off-by: peefy --- kclvm/compiler/src/codegen/llvm/node.rs | 90 ++++++++++++++---- kclvm/runtime/src/_kclvm.bc | Bin 14624 -> 14584 bytes kclvm/runtime/src/_kclvm.h | 6 +- kclvm/runtime/src/_kclvm.ll | 10 +- kclvm/runtime/src/_kclvm.rs | 1 + kclvm/runtime/src/_kclvm_addr.rs | 1 + kclvm/runtime/src/_kclvm_api_spec.rs | 12 ++- kclvm/runtime/src/value/api.rs | 14 +++ kclvm/runtime/src/value/val_dict.rs | 9 ++ .../config_op/override/override_3/main.k | 10 ++ .../override/override_3/stdout.golden | 7 ++ .../config_op/override/override_4/main.k | 10 ++ .../override/override_4/stdout.golden | 2 + 13 files changed, 139 insertions(+), 33 deletions(-) create mode 100644 test/grammar/schema/config_op/override/override_3/main.k create mode 100644 test/grammar/schema/config_op/override/override_3/stdout.golden create mode 100644 test/grammar/schema/config_op/override/override_4/main.k create mode 100644 test/grammar/schema/config_op/override/override_4/stdout.golden diff --git a/kclvm/compiler/src/codegen/llvm/node.rs b/kclvm/compiler/src/codegen/llvm/node.rs index de58ef1a7..d5e46d4c1 100644 --- a/kclvm/compiler/src/codegen/llvm/node.rs +++ b/kclvm/compiler/src/codegen/llvm/node.rs @@ -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); @@ -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(), @@ -1436,6 +1416,7 @@ 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); @@ -1443,6 +1424,50 @@ impl<'ctx> TypedResultWalker<'ctx> for LLVMCodeGenContext<'ctx> { &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) @@ -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) } diff --git a/kclvm/runtime/src/_kclvm.bc b/kclvm/runtime/src/_kclvm.bc index c32702da5a1f7f94c930b11e8e9d078d4e8501fa..02f128b0767faa0f40df54871af6723b5b8e1a6b 100644 GIT binary patch delta 4029 zcmZWseNO(`LnDkCzp;@W3I(OQ6O4^)%jJY#+hh&_Tnj>8Q;DjzeX%&c+uCLSst$X}d z_F3mT*F0L*e?rjC7SPuD_WEWoP{*(;^sADX%I5gR=#FB;4ygKFc?D8 zVjv#v_maf{O4=O4hZo9JU|H@!n+fb@u^XnafOuc<(^-_pZvIGQOL0h3!B})M%@zK^ zc(7ghfPps&L>2v=tl=xse(r(5&3K;`NxHwIC*simjIAD<3nIp>Cs+wkjIXtseHHw<@vi|wZ6bp_v4jZ5YqB$C{3?Q?toy0)) zdjbR5lg%VVc5KKoc`P!*eCx+v2+eeAS0&8W#u5ee@PmLA5ZN6jvVp`TsM%0)sT-M- zKN79Z@hcP!|4sISd)=sZXxW*^N)FtvZ<}Bj%v9{0EV!(Ddk5Uj^n_@JQP~e!i$JOj zzwAP(@}`gKbnPWkKo5I$U^>tPPYADv$joz7bY4p${#5B_hDU7ocoGFdmK50ntLK2I zyuOmDFzR=KFblXfya7fnp0^n4p(cz%Db0VBbszmFfO_bjXWL+V*de{x3(Ie-@%~@1 z{I;bwvgQE}x-jn5aF4)Ld{hjkk9NbZIVdkXHo>#bx$JP2FGr|EpWOqMmN2bN$d$S$ z&=b+Y(40|X_6n+ovZecVk@ zzu`Z9xA4a5N0@Ez?M#IA{r%i@&S%~xHQJ-_5yh7)Kwyo(Sep-aG}l~WZg@_c&bYL7 zv=~(^!p|!qb@_afq0WJfO;H`#G$8eG)ws~h@P@M1+T8^u99&7VRsn5P3-}vc+PL(m zl6S;V2&wG6-4+kEd73AygMqV7Hg?0n{h7**kjeIG_23#!pevsEI=mZ|udl#GfU)V+ zxPuTMez$>fb$hV!H3q&rr;STsZnEQ4XUOX3Eo-`v8cxK~4amV51DSpG4Gn+DOS9hy}_DYA5tjTeH z6Z(@dthC*#dy(f8dn!^oLO$a8xiv44BB!Gq%m@&?yxYd!7l=MnV~szC)o?IO=ySkk mZG7n|SCCh@Zqt@ry*@7|cU^W-k=|N0rJrMMnqI_35&s8Mih@=E delta 4079 zcmZXXdr%Ws6vj6Xz>tUxkYEF7!Vts_AXaGsN608R3ZjC921JF}6hV9d0THPi5!B)^ zP0K@Nup$MeaXN~KpbUyARnuvG#@r@W9ybP!zo646I2Ge!8ZlzCyQYkn?o{tU>dhYhBXt@prpdheA6tD2YJ3S*jLAhf%sY< zEvN<&HVEd4X-=uh`m&UQl5zV(MWe6#3eP!8Q&MS+S+<4iq^&BDM>NOwRFb zOao~M{oIxX1HO1K7gD6N588ivJyrun~u34kBNm*OLiQ9di1`LKs{XF zNKxfF>W>yg_Ue)r-ofO%otI$yRZ4C?gY8$QU%}Y{auAXp^)WzTqc@LnX`=XOmI&v2 zteOe-4a<0f03Gd=^+9|^^%XJ?g>3=-MpZJLP%h<37;ETyXbsH22KoAGZu}0fhnGtz z%(Il*y->Z8AQn_FeWgF_`+;LzI3TK&p9O*DQl9Xj``-3YX8Vg}ST((k$H+43F|s$o z7+L8mHY6MG!pMe;cvk~-1jBzM?|2Wl63W}Kwpk73Jrc$`%LUivmIHe;;FQZ*8XLxP z?>yb%X8HCAYXMj&NtMc zFNJ)qiLq1+1iT;U-_8Iu^KJ9gCjh?T^So;^9fJk&)jhhrPz3n;^VJP-x_%ou`-oLP zYyY8dnAeRbe}?sWdYK{=@)gV=_X9rVc~5;B;3J>#o_YbkqJJv7fPPeqVLN*s1ni8y zc!&)QzI8Zg*Gdq$jxYKJ>stYTb_6`IRgE8LY;RMuC~!NzR4s0G+>YkUk*p6UPl3Qn zmFQy~l$!j3Ex^8JpQPpGLJ8eN`g9={ZLInu^PG-4odM#s^xy8sK8+6MK5T^Atnbt> zgWe6Lr{saahEGEzT@y2*q$Z>22K+Y4&EA&{Kk-`6#MQZi;(B?%JON5QvMCAkZm_Ef z;|s`Ge2m~S?ti*FeL`EH$3n69UFfm#bN&b{uFCJ@wn9F`Q-7?s&T4Nm_+}FXfr|g` zxf>W@>uMDOMDMK+JtrT8cGo0MRa04vBhYJfg0c51cEZ~=DcA`IXPv?_ypLxXKgNFs zGmLxXRBXv`jui=wAN9KhQP6@HloaF3tjT)MlTxpzMYm;SDetJcCsh-RLMoJtRj?+i ze5;RxZKi0kPX_3FW8mcFp*lwddaTFoUli-DKx=ll5jdI1`I;|bY(#T&{3s6o>up@v zpxmX!u(tiePhn{s60dV-eos*Jz&@ws?1PH+9{*whHr0x|-B>rrY{+Pb`t^hxU|`d* zI2GzgmR0{jK}%&97S#a(hbyY#%oe)&e8ia9=8^s32iVAN%F8HW{6CDgdH8Uvc26US z%W8tykEHI%@ZoaiekYf_){VPPvbm9uT;QO$@4LDCSM;#!r&vu{Eg*=)&IIAXsr|d7 zcG?SGM_bT6XSr5WsLk1wy*8^LyJ(|kleTcfy1e3zYi-Mg8m;;DCpmeVj 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, diff --git a/kclvm/runtime/src/_kclvm_api_spec.rs b/kclvm/runtime/src/_kclvm_api_spec.rs index 7cbfc4f4b..0b902e34e 100644 --- a/kclvm/runtime/src/_kclvm_api_spec.rs +++ b/kclvm/runtime/src/_kclvm_api_spec.rs @@ -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); @@ -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); @@ -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); diff --git a/kclvm/runtime/src/value/api.rs b/kclvm/runtime/src/value/api.rs index 5d211a320..2a0c6d2c1 100644 --- a/kclvm/runtime/src/value/api.rs +++ b/kclvm/runtime/src/value/api.rs @@ -1050,6 +1050,20 @@ 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); + matches!( + p.dict_get_attr_operator(key), + Some(ConfigEntryOperationKind::Override) + ) as kclvm_bool_t +} + #[no_mangle] #[runtime_fn] pub unsafe extern "C" fn kclvm_dict_get( diff --git a/kclvm/runtime/src/value/val_dict.rs b/kclvm/runtime/src/value/val_dict.rs index 8c7a6a0f9..dea9cd89e 100644 --- a/kclvm/runtime/src/value/val_dict.rs +++ b/kclvm/runtime/src/value/val_dict.rs @@ -116,6 +116,15 @@ 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 { + 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 entry e.g., {k1: v1, k2, v2}.get_entry(k1) == {k1: v1} pub fn dict_get_entry(&self, key: &str) -> Option { match &*self.rc.borrow() { diff --git a/test/grammar/schema/config_op/override/override_3/main.k b/test/grammar/schema/config_op/override/override_3/main.k new file mode 100644 index 000000000..a37f015f8 --- /dev/null +++ b/test/grammar/schema/config_op/override/override_3/main.k @@ -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} diff --git a/test/grammar/schema/config_op/override/override_3/stdout.golden b/test/grammar/schema/config_op/override/override_3/stdout.golden new file mode 100644 index 000000000..ad5c75267 --- /dev/null +++ b/test/grammar/schema/config_op/override/override_3/stdout.golden @@ -0,0 +1,7 @@ +instance: + field1: + - a: 1 + b: key + c: + key: value + d: value diff --git a/test/grammar/schema/config_op/override/override_4/main.k b/test/grammar/schema/config_op/override/override_4/main.k new file mode 100644 index 000000000..6ebce8331 --- /dev/null +++ b/test/grammar/schema/config_op/override/override_4/main.k @@ -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} diff --git a/test/grammar/schema/config_op/override/override_4/stdout.golden b/test/grammar/schema/config_op/override/override_4/stdout.golden new file mode 100644 index 000000000..044cef528 --- /dev/null +++ b/test/grammar/schema/config_op/override/override_4/stdout.golden @@ -0,0 +1,2 @@ +instance: + field1: null