-
PrimerThis is related to runtime functions like map loads that might return nill. e.g. map<int> marks = {sam: 50, jon: 60};
int? val = marks["jake"]; corresponding BIR snippet %1(LOCAL) map<int>;
%8(LOCAL) int | ();
%10(TEMP) string;
%1 = NewMap %2;
%10 = ConstLoad jake;
%8 = %1[%10]; Also note that for struct smtPtr {
int32_t type_string_table_index;
void * value;
}
Options 1Runtime's map_load method will return a #[no_mangle]
pub extern "C" fn map_load_int(ptr: *mut BalMapInt, key: *mut BString, output_val: *mut i32) -> bool {
// Load BalMap from pointer
assert!(!ptr.is_null());
let bal_map = unsafe { &mut *ptr };
// Load Key C string
assert!(!key.is_null());
let key_str = unsafe { (*key).value };
// Output param
assert!(!output_val.is_null());
match bal_map.get(key_str) {
Some(val) => {
unsafe { *output_val = val.clone() };
true
}
None => false,
}
} On the codegen side we need to branch depending on the return value. However generating a branch instruction from the codegen side is rather complicated currently as MapLoadInstruction is modeled as a non-terminating instruction. A sample code for map type load instruction would look something like below. void MapLoadInsn::translate(LLVMModuleRef &modRef) {
const auto &funcObj = getFunctionRef();
auto builder = funcObj.getLLVMBuilder();
LLVMValueRef lhs = funcObj.getLLVMLocalOrGlobalVar(getLhsOperand());
LLVMValueRef outParam = LLVMBuildAlloca(builder, LLVMInt32Type(), "_out_param");
LLVMValueRef params[] = {funcObj.createTempVariable(rhsOp), funcObj.createTempVariable(keyOp), outParam};
[[maybe_unused]] LLVMValueRef retVal = LLVMBuildCall(builder, getMapLoadDeclaration(modRef), params, 3, "");
// if retVal is true
getFunctionMutableRef().addValueToSmartStruct(modRef, outParam, Type(TYPE_TAG_INT, ""), lhs);
// otherwise generate this code
// getFunctionMutableRef().addValueToSmartStruct(modRef, getPackageRef().getGlobalNilVar(), Type(TYPE_TAG_NIL, ""), lhs);
} Here we would need to check The We can't do the same for map load here because, we need the BIR class information to generate the code that goes to the two branches (i.e. load the correct values to LHS's smtPtr). Summary:
Options 2Pass the #[repr(C)]
pub struct smtPtr {
str_table_offset: i32,
val: *mut c_void,
}
#[no_mangle]
pub extern "C" fn map_load_int(ptr: *mut BalMapInt, key: *mut BString, output_val: *mut smtPtr) -> bool {
// Load BalMap from pointer
assert!(!ptr.is_null());
let bal_map = unsafe { &mut *ptr };
// Load Key C string
assert!(!key.is_null());
let key_str = unsafe { (*key).value };
// Output param
assert!(!output_val.is_null());
let output_val = unsafe { &mut *output_val };
match bal_map.get(key_str) {
Some(val) => {
output_val.val = Box::into_raw(Box::new(val.clone())) as *mut c_void;
// TODO set string table offset to type
true
}
None => {
output_val.val = Box::into_raw(Box::new(0)) as *mut c_void;
// TODO set string table offset to type
false
}
}
} This can't be completed currently because the mangled type string table is managed on the LLVM codegen side. Thus it would be have to be moved to the runtime. Summary:
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 7 replies
-
@ayonam , @manuranga , @ramakotareddy , @shubhamnarlawar and @KavinduZoysa |
Beta Was this translation helpful? Give feedback.
-
@ruvi-d, under the 2nd option, I cannot understand the reason you suggest to move allocating |
Beta Was this translation helpful? Give feedback.
-
Since we want to minimize the Rust usage for non std lib components; I suppose option 1 would be the preference. We can try @manuranga's suggesting to create a pass after BIR parsing to identify map loading instructions and inject new branching instructions and basic blocks. |
Beta Was this translation helpful? Give feedback.
-
Option 1 is now easily viable (as of #282 ) and will be considered as the idiomatic approach |
Beta Was this translation helpful? Give feedback.
Option 1 is now easily viable (as of #282 ) and will be considered as the idiomatic approach